仅用OmniAuth实现Devise登录:跳过本地登录页直接跳转授权方
解决方案
重写Devise::SessionsController的new方法完全可行,这是最直接且符合需求的方案,另外还有一种全局拦截的备选方案,具体如下:
方案一:重写自定义SessionsController(推荐)
这个方案只针对登录页请求做处理,逻辑清晰,不会干扰其他路由:
- 创建自定义的会话控制器,继承Devise的SessionsController:
# app/controllers/users/sessions_controller.rb class Users::SessionsController < Devise::SessionsController def new # 渲染自动提交的POST表单,直接触发Keycloak授权流程 render inline: <<~HTML <form method="post" action="#{user_keycloakopenid_omniauth_authorize_path}" data-turbo="false"> <input type="hidden" name="authenticity_token" value="#{form_authenticity_token}"> <script>document.querySelector('form').submit();</script> </form> HTML end end
- 修改路由配置,让Devise使用这个自定义控制器:
# config/routes.rb devise_for :users, controllers: { sessions: 'users/sessions' }
- 删除原有的
app/views/devise/sessions/new.html.erb文件即可。
方案二:全局拦截未登录请求(备选)
如果不想重写控制器,可以在全局前置过滤器中处理,但需要注意排除登录回调和登录本身的路由,避免重定向循环:
# app/controllers/application_controller.rb class ApplicationController < ActionController::Base before_action :redirect_to_keycloak_login, unless: :user_signed_in? private def redirect_to_keycloak_login # 排除登录相关路由,防止循环重定向 skip_paths = [user_session_path, user_keycloakopenid_omniauth_callback_path] return if skip_paths.any? { |path| request.path.start_with?(path) } # 自动提交POST表单到Keycloak授权路径 render inline: <<~HTML <form method="post" action="#{user_keycloakopenid_omniauth_authorize_path}" data-turbo="false"> <input type="hidden" name="authenticity_token" value="#{form_authenticity_token}"> <script>document.forms[0].submit();</script> </form> HTML false end end
关键说明
不能直接在new方法中用redirect_to跳转,因为OmniAuth的授权接口默认要求POST请求(安全设计,防范CSRF攻击),GET请求会直接报错。自动提交表单是最稳妥的触发POST请求的方式。
内容的提问来源于stack exchange,提问作者Toto
相关产品推荐
相关产品推荐

