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

Rails API中Devise JWT认证问题:登录报错与最佳实践咨询

问题解决方案

1. 解决Devise重定向问题,适配API登录流程

核心原因

Devise默认会对navigational_formats配置中的请求格式执行重定向逻辑,你的配置里包含了:json,导致登录成功后尝试调用users_url生成跳转路径,而API场景不需要重定向。

修复步骤

  • 修改devise.rb配置:移除navigational_formats中的:json,让Devise不对JSON请求触发重定向:
    config.navigational_formats = ['*/*', :html] # 去掉:json
    
  • 修正SessionsController的create方法:warden.authenticate!认证失败会直接抛出异常,原代码的else分支永远不会执行,需要捕获异常并返回错误响应:
    class Api::V1::SessionsController < Devise::SessionsController
      respond_to :json
      skip_before_action :verify_signed_out_user, only: :destroy
    
      def create
        self.resource = warden.authenticate!(auth_options)
        token = generate_jwt_token(resource)
        render json: { data: { user: resource.as_json(only: [:id, :email]), token: token } }, status: :ok
      rescue Warden::AuthenticationFailed
        render json: { errors: ['Invalid email or password'] }, status: :unauthorized
      end
    
      def destroy
        sign_out(resource_name)
        render json: { message: 'Signed out successfully' }, status: :ok
      end
    
      private
    
      def generate_jwt_token(user)
        JWT.encode(
          { user_id: user.id, exp: 1.day.from_now.to_i },
          Rails.application.credentials.devise_jwt_secret_key
        )
      end
    end
    

2. Devise处理JWT的最佳实践

推荐使用官方维护的devise-jwt gem,避免手动编码JWT带来的安全和维护问题:

  1. 添加依赖:在Gemfile中加入gem 'devise-jwt',执行bundle install
  2. 配置User模型:集成JWT模块,可选添加令牌吊销策略:
    class User
      include Mongoid::Document
      devise :database_authenticatable, :registerable,
             :recoverable, :rememberable, :validatable,
             :jwt_authenticatable, jwt_revocation_strategy: JwtBlacklist
    end
    
  3. 完善devise.rb的JWT配置:
    config.jwt do |jwt|
      jwt.secret = Rails.application.credentials.devise_jwt_secret_key
      jwt.expiration_time = 1.day.to_i
      # 指定登录接口返回JWT
      jwt.dispatch_requests = [['POST', %r{^/api/v1/users/sign_in$}]]
      # 指定登出接口吊销JWT
      jwt.revocation_requests = [['DELETE', %r{^/api/v1/users/sign_out$}]]
      # 自定义JWT负载内容
      jwt.token_payload do |user, _request|
        { user_id: user.id, email: user.email }
      end
    end
    
  4. 令牌验证:在需要认证的API控制器中添加before_action :authenticate_user!,devise-jwt会自动解析Authorization: Bearer <token>头并验证有效性。

3. 结构化API响应规范

统一响应格式,方便前端解析:

  • 登录成功(200 OK):
    {
      "data": {
        "user": {
          "id": 1,
          "email": "user@example.com"
        },
        "token": "eyJhbGciOiJIUzI1NiJ9..."
      }
    }
    
  • 登录失败(401 Unauthorized):
    {
      "errors": ["Invalid email or password"]
    }
    
  • 登出成功(200 OK):
    {
      "message": "Signed out successfully"
    }
    
  • 参数错误(422 Unprocessable Entity):
    {
      "errors": ["Email can't be blank", "Password can't be blank"]
    }
    

额外配置建议

  • 路由配置:确保API版本的Devise路由正确指向自定义控制器:
    namespace :api do
      namespace :v1 do
        devise_for :users, controllers: { sessions: 'api/v1/sessions' }
      end
    end
    
  • 用户信息脱敏:在User模型中重写as_json,只返回必要字段:
    class User
      def as_json(options = {})
        super(only: [:id, :email, :created_at])
      end
    end
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 13:29:52