在 rails 应用程序中集成 SAML
Integrate SAML in rails application
我正在尝试在我的项目中添加 ruby-saml。但是我对如何在我的场景中实现它有点困惑。我在一个网站上,比方说 abc.com 并且有一个按钮。当我点击按钮时,我需要重定向到网站 xyz.com,我需要在其中传递 SAML XML 并将其发送到 xyz.com/SAML。 SAML 请求将由 xyz.com 处理,然后他们会向我发送响应。谁能告诉我如何实现它?
另外,我对这些字段感到困惑。有人可以给我一个简短的总结吗?
settings.assertion_consumer_service_url
settings.sp_entity_id
settings.idp_entity_id
settings.idp_sso_service_url
settings.idp_slo_service_url
def init
request = OneLogin::RubySaml::Authrequest.new
saml_settings.name_identifier_value_requested = "testuser@example.com"
saml_settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
redirect_to(request.create(saml_settings))
end
def saml_settings
settings = OneLogin::RubySaml::Settings.new
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
settings.idp_sso_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
# Optional for most SAML IdPs
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
# or as an array
settings.authn_context = [
"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
"urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
]
# Optional bindings (defaults to Redirect for logout POST for ACS)
settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
settings
end
你想:
- a.) 从其他服务(如 Microsoft Azure Directory、OneLogin 等)启用 SAML 登录? (那么你就是SP = Service Provider)
- b.) 您的应用拥有用户并为其他应用提供登录服务(IDP = 身份提供商)
我想是 a)?
然后它取决于:它是每个客户的提供商,还是动态的?每个客户可以配置自己的 IDP,还是为整个应用程序固定一个 IDP?
如果是后者,那么我强烈建议改用 omniauth-saml
,这样更容易配置。
但是如果您想使用企业登录每个客户,那么我们就是这样做的。
- 首先:我从来没有纠结过所有的具体设置和 urls,Saml 可以通过“元数据 url”自动配置自己,我们让我们的客户指定一个元数据 uri,该 uri 有我们需要的所有信息,因此我们可以使用提供的 class:
来解析它
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
settings = idp_metadata_parser.parse_remote(organisation.idp_meta_data_url)
然后我们在其中添加自己的设置+路由信息:
settings.assertion_consumer_service_url = "https://#{request.host}/saml/consume/#{organisation.id}"
settings.issuer = "https://#{@request.host}/saml/metadata/#{organisation.id}"
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
# Optional for most SAML IdPs
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
# You would need a normal certificate/private key to enable signature stuff
settings.certificate = File.read('config/saml/certificate.crt')
settings.private_key = File.read('config/saml/private_key.key')
# In our case customer can optional activate signature validation:
if organisation.signature_enabled?
settings.security[:authn_requests_signed] = true # Enable or not signature on AuthNRequest
settings.security[:logout_requests_signed] = true # Enable or not signature on Logout Request
settings.security[:logout_responses_signed] = true # Enable or not signature on Logout Response
settings.security[:want_assertions_signed] = true # Enable or not the requirement of signed assertion
settings.security[:metadata_signed] = true # Enable or not signature on Metadata
settings.security[:digest_method] = XMLSecurity::Document::SHA1
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
end
请求/响应
这些或多或少来自 ruby-saml 示例:
控制器:
skip_before_action :verify_authenticity_token
def init
@saml_request = OneLogin::RubySaml::Authrequest.new
redirect_url = @saml_request.create(saml_settings)
if redirect_uri
redirect_to(redirect_uri)
else
@error = t('saml_controller.error')
render 'error'
end
end
def consume
@saml_response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
@saml_response.settings = saml_settings
if @saml_response.is_valid?
# do application logic, create user/update user sign in user
sign_in(....)
session[:session_valid_for] = 12.hours.from_now.to_i
redirect_to '/'
else
redirect_to '/watcher/profile'
end
else
@error = @saml_response.errors
render 'error'
end
end
元数据
大多数客户也需要元数据 uri,以便将 SP 添加到他们的 IDP 中(并自动配置该部分),因此您还需要提供元数据,例如"/saml/#{org.id}/metadata" 和 return 元数据:
def metadata
meta = OneLogin::RubySaml::Metadata.new
render xml: meta.generate(saml_settings), content_type: "application/samlmetadata+xml"
end
更新:使用 omniauth-saml
我们还在一个应用程序中使用了 Omniauth saml。这很简单,但配置取决于您要集成的服务器。您将需要一个 sso 目标 url(来自另一端的消费 url)和一个证书指纹或安全证书。您还可以指定“名称标识符”、电子邮件或用户名或类似的东西。
Rails.application.config.middleware.use OmniAuth::Builder do
use OmniAuth::Strategies::SAML,
idp_sso_target_url: "??",
idp_slo_target_url: "??",
idp_cert_fingerprint: "??",
name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
# or "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" for E-Mail
issuer: "YourAppName.com"
查看 omniauth-saml 的 good Readme doc 以获得所有选项的列表。您还可以使用 request_attributes
:
从 OneLogin/IDP 请求更多属性
:request_attributes - Used to build the metadata file to inform the
IdP to send certain attributes along with the SAMLResponse messages.
Defaults to requesting name, first_name, last_name and email
attributes. See the OneLogin::RubySaml::AttributeService class in the
Ruby SAML gem for the available options for each attribute.
更新:将动态参数传递给 omniauth saml 到 Idp 的重定向:
Omniauth-saml 中似乎没有动态参数选项,因此您可以尝试 patch/override 行为。 Omniauth-SAML 是一种机架中间件,因此您只能访问请求对象,而不能访问正常的 Rails 内容。如果您信任您的用户(您不应该信任),那么您可以将信息放入 omniauth 的参数中:/auth/saml?something1=foo&bar=2
,或者您可以使用 ActiveSupport Message Encryptor 加密参数。
如果您知道如何从请求中提取动态参数,则可以动态应用此补丁,因为 Ruby!
# put this into
#
# config/initializers/omniauth_patch.rb
#
module OmniauthPatch
def additional_params_for_authn_request
# here you should have access to the current request
# try around with binding.irb what you can do
binding.irb
# return parameters you want to pass to the saml redirect
{
email: email,
# ...
}
end
end
OmniAuth::Strategies::SAML.include OmniauthPatch
我正在尝试在我的项目中添加 ruby-saml。但是我对如何在我的场景中实现它有点困惑。我在一个网站上,比方说 abc.com 并且有一个按钮。当我点击按钮时,我需要重定向到网站 xyz.com,我需要在其中传递 SAML XML 并将其发送到 xyz.com/SAML。 SAML 请求将由 xyz.com 处理,然后他们会向我发送响应。谁能告诉我如何实现它?
另外,我对这些字段感到困惑。有人可以给我一个简短的总结吗?
settings.assertion_consumer_service_url
settings.sp_entity_id
settings.idp_entity_id
settings.idp_sso_service_url
settings.idp_slo_service_url
def init
request = OneLogin::RubySaml::Authrequest.new
saml_settings.name_identifier_value_requested = "testuser@example.com"
saml_settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
redirect_to(request.create(saml_settings))
end
def saml_settings
settings = OneLogin::RubySaml::Settings.new
settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume"
settings.idp_sso_service_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
settings.idp_sso_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
# Optional for most SAML IdPs
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
# or as an array
settings.authn_context = [
"urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport",
"urn:oasis:names:tc:SAML:2.0:ac:classes:Password"
]
# Optional bindings (defaults to Redirect for logout POST for ACS)
settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
settings
end
你想:
- a.) 从其他服务(如 Microsoft Azure Directory、OneLogin 等)启用 SAML 登录? (那么你就是SP = Service Provider)
- b.) 您的应用拥有用户并为其他应用提供登录服务(IDP = 身份提供商)
我想是 a)?
然后它取决于:它是每个客户的提供商,还是动态的?每个客户可以配置自己的 IDP,还是为整个应用程序固定一个 IDP?
如果是后者,那么我强烈建议改用 omniauth-saml
,这样更容易配置。
但是如果您想使用企业登录每个客户,那么我们就是这样做的。
- 首先:我从来没有纠结过所有的具体设置和 urls,Saml 可以通过“元数据 url”自动配置自己,我们让我们的客户指定一个元数据 uri,该 uri 有我们需要的所有信息,因此我们可以使用提供的 class: 来解析它
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
settings = idp_metadata_parser.parse_remote(organisation.idp_meta_data_url)
然后我们在其中添加自己的设置+路由信息:
settings.assertion_consumer_service_url = "https://#{request.host}/saml/consume/#{organisation.id}"
settings.issuer = "https://#{@request.host}/saml/metadata/#{organisation.id}"
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
# Optional for most SAML IdPs
settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
# You would need a normal certificate/private key to enable signature stuff
settings.certificate = File.read('config/saml/certificate.crt')
settings.private_key = File.read('config/saml/private_key.key')
# In our case customer can optional activate signature validation:
if organisation.signature_enabled?
settings.security[:authn_requests_signed] = true # Enable or not signature on AuthNRequest
settings.security[:logout_requests_signed] = true # Enable or not signature on Logout Request
settings.security[:logout_responses_signed] = true # Enable or not signature on Logout Response
settings.security[:want_assertions_signed] = true # Enable or not the requirement of signed assertion
settings.security[:metadata_signed] = true # Enable or not signature on Metadata
settings.security[:digest_method] = XMLSecurity::Document::SHA1
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
end
请求/响应
这些或多或少来自 ruby-saml 示例:
控制器:
skip_before_action :verify_authenticity_token
def init
@saml_request = OneLogin::RubySaml::Authrequest.new
redirect_url = @saml_request.create(saml_settings)
if redirect_uri
redirect_to(redirect_uri)
else
@error = t('saml_controller.error')
render 'error'
end
end
def consume
@saml_response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
@saml_response.settings = saml_settings
if @saml_response.is_valid?
# do application logic, create user/update user sign in user
sign_in(....)
session[:session_valid_for] = 12.hours.from_now.to_i
redirect_to '/'
else
redirect_to '/watcher/profile'
end
else
@error = @saml_response.errors
render 'error'
end
end
元数据
大多数客户也需要元数据 uri,以便将 SP 添加到他们的 IDP 中(并自动配置该部分),因此您还需要提供元数据,例如"/saml/#{org.id}/metadata" 和 return 元数据:
def metadata
meta = OneLogin::RubySaml::Metadata.new
render xml: meta.generate(saml_settings), content_type: "application/samlmetadata+xml"
end
更新:使用 omniauth-saml
我们还在一个应用程序中使用了 Omniauth saml。这很简单,但配置取决于您要集成的服务器。您将需要一个 sso 目标 url(来自另一端的消费 url)和一个证书指纹或安全证书。您还可以指定“名称标识符”、电子邮件或用户名或类似的东西。
Rails.application.config.middleware.use OmniAuth::Builder do
use OmniAuth::Strategies::SAML,
idp_sso_target_url: "??",
idp_slo_target_url: "??",
idp_cert_fingerprint: "??",
name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
# or "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" for E-Mail
issuer: "YourAppName.com"
查看 omniauth-saml 的 good Readme doc 以获得所有选项的列表。您还可以使用 request_attributes
:
:request_attributes - Used to build the metadata file to inform the IdP to send certain attributes along with the SAMLResponse messages. Defaults to requesting name, first_name, last_name and email attributes. See the OneLogin::RubySaml::AttributeService class in the Ruby SAML gem for the available options for each attribute.
更新:将动态参数传递给 omniauth saml 到 Idp 的重定向:
Omniauth-saml 中似乎没有动态参数选项,因此您可以尝试 patch/override 行为。 Omniauth-SAML 是一种机架中间件,因此您只能访问请求对象,而不能访问正常的 Rails 内容。如果您信任您的用户(您不应该信任),那么您可以将信息放入 omniauth 的参数中:/auth/saml?something1=foo&bar=2
,或者您可以使用 ActiveSupport Message Encryptor 加密参数。
如果您知道如何从请求中提取动态参数,则可以动态应用此补丁,因为 Ruby!
# put this into
#
# config/initializers/omniauth_patch.rb
#
module OmniauthPatch
def additional_params_for_authn_request
# here you should have access to the current request
# try around with binding.irb what you can do
binding.irb
# return parameters you want to pass to the saml redirect
{
email: email,
# ...
}
end
end
OmniAuth::Strategies::SAML.include OmniauthPatch