使用ruby-saml实现OmniAuth时request.env['omniauth.auth']为空如何解决
问题核心原因
你当前的SAML认证流程完全基于ruby-saml手动实现,没有走OmniAuth的标准处理流程,因此OmniAuth中间件不会自动解析SAML响应、生成omniauth.auth和omniauth.params数据,这就是你取值为nil的根本原因。Google OAuth2流程走了OmniAuth注册的provider,所以可以正常获取数据。
你需要动态配置SAML的idp_sso_target_url和idp_cert的需求,完全可以通过OmniAuth SAML的动态配置能力实现,无需自己手动实现整套SAML逻辑。
具体解决步骤
1. 新增依赖
在Gemfile中添加omniauth-saml依赖,执行bundle install:
gem 'omniauth-saml'
2. 修改OmniAuth初始化配置
在OmniAuth初始化文件中注册SAML provider,通过setup参数实现动态配置:
Rails.application.config.middleware.use OmniAuth::Builder do provider :google_oauth2, CONFIG[:google_client_id], CONFIG[:google_client_secret], access_type: 'online' # 新增SAML provider配置 provider :saml, # 动态配置回调,可从请求、数据库、配置文件读取动态参数 setup: ->(env) { request = Rack::Request.new(env) opts = env['omniauth.strategy'].options # 动态IDP配置,你后续需要改参数直接修改这里的取值逻辑即可 opts[:idp_sso_target_url] = CONFIG[:saml_sso_target] opts[:idp_cert] = CONFIG[:saml_cleint_certificate] # 固定配置 opts[:assertion_consumer_service_url] = "#{request.base_url}/auth/saml/callback" opts[:issuer] = '1234567890' opts[:name_identifier_format] = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' opts[:idp_cert_fingerprint_algorithm] = 'http://www.w3.org/2000/09/xmldsig#sha256' }, callback_path: '/auth/saml/callback' on_failure { |env| Authentication::OmniauthController.action(:failure).call(env) } end # 允许POST请求发起SAML认证,适配你的表单提交逻辑 OmniAuth.config.allowed_request_methods = [:get, :post] OmniAuth.config.logger = Rails.logger
3. 调整路由
删除你之前自定义的SAML相关路由,新增SAML回调路由:
# 删除以下两行自定义路由 # post 'auth/saml', to: 'authentication/saml#saml', as: :saml_authorize # post 'auth/saml/callback', to: 'authentication/saml#callback' # 新增SAML回调路由,和Google OAuth2的回调逻辑统一处理 get '/auth/saml/callback' => 'authentication/omniauth#saml' get '/auth/google_oauth2/callback' => 'authentication/omniauth#google_oauth2' get 'omniauth/failure' => 'authentication/omniauth#failure'
4. 调整视图表单
将表单提交地址改为OmniAuth标准的SAML发起路径:
<%= form_tag '/auth/saml', class: 'text-center' do %> <%= select_tag :user_type, options_for_select([[t('.option_user'), 'user'], [t('.option_serviceuser'), 'service_user']], sel_obj_class_name), class: 'js-select-user-type platform-select-service' %> <button class="btn btn-light"> SAML Auth </button> <% end %>
5. 新增SAML回调处理逻辑
在Authentication::OmniauthController中新增SAML回调方法,直接从env中取OmniAuth生成的标准数据即可:
def saml # 这里可以正常拿到你表单提交的user_type参数 user_type = request.env['omniauth.params']['user_type'] # 标准OmniAuth认证信息 auth_hash = request.env['omniauth.auth'] user = if user_type == 'service_user' User.from_omniauth(auth_hash) else User.from_omniauth(auth_hash) end if user flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Saml' sign_in_and_redirect user, event: :authentication else redirect_to signin_path, alert: t('devise.failure.invalid') end end
你之前手动实现的SamlController可以直接废弃,OmniAuth SAML已经封装了SAML请求生成、响应校验、数据解析的全部逻辑,还能兼容你动态配置IDP参数的需求。
内容的提问来源于stack exchange,提问作者Umes Bastola
相关产品推荐
相关产品推荐

