如何在Rails中实现OAuth提供者,供单客户端跨域论坛使用
嘿,我完全懂你的困扰——网上大多OAuth教程都是教你对接Twitter/Facebook这类第三方登录,自建Provider的资料确实少得可怜。不过别怕,用Rails实现一个单客户端的OAuth2提供者其实没那么复杂,我给你一步步拆解:
一、主站(OAuth2 提供者)配置
我们用Doorkeeper这个专门为Rails打造的OAuth2提供者gem,它能帮我们快速搞定授权流程、token管理这些核心功能。
添加并初始化Doorkeeper
- 在主站的
Gemfile里加入:gem 'doorkeeper' gem 'rack-cors' # 跨域支持,因为论坛和主站域名不同 - 执行
bundle install,然后生成Doorkeeper的配置文件和数据库迁移:
这会创建rails generate doorkeeper:install rails db:migrateoauth_applications(存储客户端信息)、oauth_access_tokens(存储令牌)等数据表。
- 在主站的
配置Doorkeeper核心规则
打开config/initializers/doorkeeper.rb,做以下关键配置:- 指定用户登录验证逻辑(确保只有主站已登录用户才能授权):
resource_owner_authenticator do # 这里用你主站现有的current_user方法和登录路由 current_user || redirect_to(new_user_session_path, alert: "请先登录主站账号") end - 限制单客户端访问(因为你只给论坛用):
# 允许的回调地址,替换成你的论坛回调URL allowed_redirect_uri = "https://your-forum-domain.com/auth/main_site/callback" # 强制验证回调地址,防止恶意跳转 enforce_configured_redirect_uris true - 设置授权范围(只给论坛必要的用户信息):
default_scopes :openid, :profile optional_scopes :email
- 指定用户登录验证逻辑(确保只有主站已登录用户才能授权):
创建论坛客户端应用
你可以通过Doorkeeper自带的后台(访问主站的/oauth/applications路径)手动创建,或者在rails控制台里生成:OauthApplication.create!( name: "社区论坛", redirect_uri: "https://your-forum-domain.com/auth/main_site/callback", scopes: "openid profile email", confidential: true # 客户端是web应用,设为true更安全 )创建后会得到
client_id和client_secret,后面论坛配置要用到。配置跨域(CORS)
打开config/initializers/cors.rb,添加允许论坛域名的跨域请求:Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins "https://your-forum-domain.com" # 替换成你的论坛域名 resource "*", headers: :any, methods: [:get, :post, :options] end end提供用户信息接口
论坛拿到令牌后需要获取用户信息,所以在主站加一个API接口:- 创建
app/controllers/api/v1/users_controller.rb:class Api::V1::UsersController < ApplicationController before_action :doorkeeper_authorize! # 验证令牌有效性 def me render json: { uid: current_user.id, name: current_user.name, email: current_user.email # 其他你想同步到论坛的字段 } end end - 在
config/routes.rb里添加路由:namespace :api do namespace :v1 do get 'users/me', to: 'users#me' end end - 最后在Doorkeeper配置里指定这个接口的逻辑:
resource_owner_from_access_token do |token| User.find_by(id: token.resource_owner_id) end
- 创建
二、论坛(OAuth2 客户端)配置
论坛这边用OmniAuth来对接我们的自定义OAuth2提供者。
添加OmniAuth相关gem
在论坛的Gemfile里加入:gem 'omniauth' gem 'omniauth-oauth2'执行
bundle install。配置OmniAuth对接主站
创建config/initializers/omniauth.rb:Rails.application.config.middleware.use OmniAuth::Builder do provider :oauth2, "YOUR_CLIENT_ID", # 主站创建客户端时拿到的client_id "YOUR_CLIENT_SECRET", # 对应的client_secret { site: "https://your-main-site-domain.com", # 主站域名 authorize_url: "/oauth/authorize", # Doorkeeper默认授权地址 token_url: "/oauth/token", # Doorkeeper默认令牌地址 user_info_url: "/api/v1/users/me", # 我们刚才在主站加的用户信息接口 callback_path: "/auth/main_site/callback" # 论坛的回调路径 } end处理授权回调逻辑
创建或修改论坛的SessionsController,处理回调并同步用户:class SessionsController < ApplicationController def create auth = request.env['omniauth.auth'] # 根据主站返回的uid查找或创建论坛用户 user = User.find_or_create_by(uid: auth['uid']) do |u| u.email = auth['info']['email'] u.name = auth['info']['name'] # 可以设置默认密码或者禁用本地登录,因为用户用主站账号登录 u.password = SecureRandom.hex(16) u.password_confirmation = u.password end # 登录用户 sign_in_and_redirect user, notice: "登录成功!" end def failure redirect_to root_path, alert: "登录失败:#{params[:message]}" end end然后在
config/routes.rb里添加回调路由:get '/auth/:provider/callback', to: 'sessions#create' get '/auth/failure', to: 'sessions#failure'添加登录入口
在论坛的登录页面添加一个按钮链接:<%= link_to "使用主站账号登录", "/auth/main_site", class: "btn btn-primary" %>
三、关键安全注意事项
- 必须用HTTPS:OAuth涉及敏感令牌和用户信息,主站和论坛都要启用HTTPS,避免明文传输。
- 严格限制回调地址:主站Doorkeeper配置里一定要锁定论坛的回调URL,防止钓鱼攻击。
- 令牌有效期设置:在Doorkeeper配置里调整
access_token_expires_in,比如设为1小时,同时启用refresh token来实现自动续期。 - 最小权限原则:只给论坛必要的用户信息,不要暴露敏感字段(比如主站的用户密码、隐私数据)。
这样一套流程走下来,你的论坛用户就能通过主站账号一键登录了,完全符合你的封闭社区需求~
内容的提问来源于stack exchange,提问作者Kenn

