You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Devise实现两个共享数据库的Rails应用跨应用用户认证

解决方案

方案1:基于共享密钥的请求签名(最适配共享DB场景)

既然两个应用共享同一个数据库,直接用用户ID + 共享密钥 + 时间戳生成签名即可,无需复杂的JWT配置:

  1. 在两个应用的配置文件(比如config/secrets.yml或环境变量)里设置完全相同的共享密钥:

    # config/secrets.yml
    shared_auth_secret: "your_secure_unique_secret_here"
    
  2. 发起请求的应用生成签名并携带参数:

    user_id = current_user.id
    timestamp = Time.now.to_i
    signature = OpenSSL::HMAC.hexdigest('SHA256', Rails.application.secrets.shared_auth_secret, "#{user_id}#{timestamp}")
    
    # 调用API时带上这三个参数
    response = HTTP.get("https://api-app.example.com/protected", params: {
      user_id: user_id,
      timestamp: timestamp,
      signature: signature
    })
    
  3. API应用侧验证签名并设置用户:

    # 在API控制器里添加前置校验
    before_action :authenticate_via_shared_secret
    
    def authenticate_via_shared_secret
      user_id = params[:user_id]
      timestamp = params[:timestamp].to_i
      signature = params[:signature]
    
      # 拦截过期请求(比如5分钟内有效,防止重放攻击)
      return head :unauthorized if (Time.now.to_i - timestamp) > 300
    
      # 重新计算签名并安全对比
      expected_signature = OpenSSL::HMAC.hexdigest('SHA256', Rails.application.secrets.shared_auth_secret, "#{user_id}#{timestamp}")
      return head :unauthorized unless ActiveSupport::SecurityUtils.secure_compare(expected_signature, signature)
    
      @current_user = User.find(user_id)
    end
    

方案2:复用Devise加密密码做验证

利用共享数据库里的encrypted_password字段,结合共享密钥生成临时验证token,无需传递明文密码:

  1. 发起请求的应用生成临时token:

    user = current_user
    temp_token = OpenSSL::HMAC.hexdigest('SHA256', Rails.application.secrets.shared_auth_secret, user.encrypted_password)
    
    response = HTTP.get("https://api-app.example.com/protected", params: {
      email: user.email,
      temp_token: temp_token
    })
    
  2. API应用侧验证token:

    before_action :authenticate_via_temp_token
    
    def authenticate_via_temp_token
      user = User.find_by(email: params[:email])
      return head :unauthorized unless user
    
      expected_token = OpenSSL::HMAC.hexdigest('SHA256', Rails.application.secrets.shared_auth_secret, user.encrypted_password)
      return head :unauthorized unless ActiveSupport::SecurityUtils.secure_compare(expected_token, params[:temp_token])
    
      @current_user = user
    end
    

方案3:修复JWT签名失败问题

你之前的JWT验证失败,90%是因为两边密钥不一致或算法/Payload不匹配:

  1. 确保两个应用使用完全相同的JWT密钥和算法,比如手动生成验证的代码:
    # 发起应用生成JWT
    payload = { user_id: current_user.id, exp: 1.hour.from_now.to_i }
    token = JWT.encode(payload, Rails.application.secrets.jwt_secret, 'HS256')
    
    # API应用验证JWT
    begin
      decoded = JWT.decode(token, Rails.application.secrets.jwt_secret, true, algorithm: 'HS256')
      @current_user = User.find(decoded[0]['user_id'])
    rescue JWT::DecodeError, ActiveRecord::RecordNotFound
      head :unauthorized
    end
    
    重点检查:密钥是否完全一致(注意大小写、特殊字符)、算法是否统一(比如都是HS256)、Payload里的exp过期时间是否有效。

关于直接传递encrypted_password的说明

Devise的encrypted_password是BCrypt哈希,它的设计只能用明文密码验证哈希,无法直接用哈希做身份校验——你需要知道用户明文密码才能验证,但传递明文密码风险极高,完全不建议这么做。

内容的提问来源于stack exchange,提问作者Jeremy Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 16:35:27