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

如何解决Keycloak+React+Rails集成中的令牌信任与角色签名问题

Keycloak集成React前端与Rails后端的令牌信任及角色传递问题

背景与当前配置

  • 已配置两个Keycloak客户端:前端为public类型,后端为private类型(因无法使用Bearer-only模式,选择private模式提升安全性)
  • 前端通过react-oidc-context成功连接Keycloak,配置如下:
const oidcConfig = {
  authority: "http://localhost:8080/realms/SecondRealm",
  client_id: "localApp-frontend",
  redirect_uri: "http://localhost:3000/"
}
  • 后端尝试用OmniAuth + keycloak-strategy配置,代码如下:
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :keycloak_openid, 'localAppBackend', Rails.application.credentials.keycloak_secret,
    client_options: {base_url: '', site: 'http://localhost:8080', realm: 'SecondRealm'},
    name: 'keycloak'
end
  • 路由配置:
get 'auth/:provider/callback', to: 'sessions#create'
get '/login', to: 'sessions#new'
  • SessionController实现:
class SessionsController < ApplicationController
  def new
    render :new
  end

  def create
    user_info = request.env['omniauth.auth']
    raise user_info # 待实现会话管理逻辑
  end
end

当前问题

  1. 前端public客户端获取的令牌未用私钥签名,后端无法完全信任,如何通过后端private客户端获取可信签名令牌?
  2. 如何调用后端Keycloak客户端,以及识别访问资源的用户?
  3. 是否有办法在用户通过前端客户端登录时,直接携带后端客户端的签名角色?

解决方案建议

1. 后端直接验证前端令牌,无需额外获取新令牌

public客户端的JWT令牌实际是由Keycloak的私钥签名的,你可以通过Keycloak realm的公钥验证令牌合法性,不需要额外走OmniAuth流程:

  • 从Keycloak realm的证书端点获取公钥
  • 在Rails后端使用jwt gem验证前端发送的Bearer令牌:
    require 'jwt'
    require 'http'
    
    def validate_token
      token = request.headers['Authorization']&.split(' ')&.last
      return render json: { error: 'Missing token' }, status: :unauthorized unless token
    
      # 从Keycloak获取公钥证书
      certs = HTTP.get("#{ENV['KEYCLOAK_URL']}/realms/#{ENV['KEYCLOAK_REALM']}/protocol/openid-connect/certs").parse
      public_key = OpenSSL::PKey::RSA.new(JWT::JWK::Set.new(certs).public_key)
      
      # 验证令牌签名、签发者等信息
      decoded = JWT.decode(token, public_key, true, 
        algorithm: 'RS256', 
        verify_iss: true, 
        iss: "#{ENV['KEYCLOAK_URL']}/realms/#{ENV['KEYCLOAK_REALM']}"
      )
      # 从令牌中提取用户ID和角色
      @current_user_id = decoded[0]['sub']
      @user_roles = decoded[0]['resource_access']['localAppBackend']['roles'] || []
    rescue JWT::DecodeError => e
      render json: { error: 'Invalid token', details: e.message }, status: :unauthorized
    end
    
  • 提前在Keycloak后端客户端配置中开启“Add to access token”选项,确保角色信息被写入令牌

2. 若坚持使用后端private客户端获取令牌:用授权码交换令牌

前端登录后,将获取的授权码传递给后端,由后端以private客户端身份向Keycloak交换可信令牌:

  • 前端登录成功后,将授权码通过HTTPS请求发送给后端接口
  • 后端向Keycloak令牌端点发起交换请求:
    require 'http'
    
    def exchange_token
      code = params[:code]
      return render json: { error: 'Missing authorization code' }, status: :bad_request unless code
    
      response = HTTP.post("#{ENV['KEYCLOAK_URL']}/realms/#{ENV['KEYCLOAK_REALM']}/protocol/openid-connect/token",
        form: {
          grant_type: 'authorization_code',
          client_id: 'localAppBackend',
          client_secret: Rails.application.credentials.keycloak_secret,
          code: code,
          redirect_uri: 'http://localhost:3000/' # 需与前端redirect_uri完全一致
        })
      
      token_data = response.parse
      # 验证交换得到的access_token,提取用户和角色信息
      validate_token(token_data['access_token'])
      # 后续可将令牌存入会话或返回给前端用于后续请求
    end
    

3. 角色传递的最佳实践

  • 在Keycloak中,给后端客户端配置的角色勾选“Add to access token”,确保角色信息被包含在令牌的resource_access.localAppBackend.roles字段中
  • 前端登录时,请求的scope包含roles,保证令牌携带角色数据
  • 后端验证令牌后,直接从对应字段提取角色,用于接口的权限控制

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 11:10:28