Rails API使用Devise-JWT无Bearer Token返回问题求助
问题:Rails API中Devise-JWT未返回Bearer Token
我在纯API类型的Rails应用中,尝试使用Devise和Devise-JWT搭建基于Token的身份认证体系,但通过Postman测试API时,响应中并未返回Bearer Token。我对Devise、Token及安全领域相关技术较为陌生,恳请提供相关指导及可行的解决方案。
现有代码
devise.rb
config.jwt do |jwt| jwt.secret = ENV['DEVISE_JWT_SECRET_KEY'] jwt.dispatch_requests = [['POST', /api/]] jwt.expiration_time = 12.hours.to_i end
routes.rb
Rails.application.routes.draw do namespace :api do devise_for :users, controllers: { sessions: 'api/sessions' } end end
api/sessions_controller.rb
class Api::SessionsController < Devise::SessionsController def create user = User.find_by('lower(email) = ?', params[:email]) if user.blank? || !user.valid_password?(params[:password]) render json: { errors: ['Invalid email/password combination'] }, status: :unauthorized return end sign_in(:user, user) render json: { user: user, token: user.token } end end
user.rb
# frozen_string_literal: true class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :trackable, :jwt_authenticatable, jwt_revocation_strategy: JwtDenylist end
jwt_denylist.rb
class JwtDenylist < ApplicationRecord include Devise::JWT::RevocationStrategies::Denylist self.table_name = 'jwt_denylist' end
cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '*', headers: :any, methods: %i[get post put patch delete options head] end end
问题原因分析
- 自定义Session控制器覆盖默认逻辑:Devise-JWT原本会自动在匹配请求中返回Token,但你自定义了
create方法,且错误调用了不存在的user.token方法——Devise-JWT并未给User模型添加该方法。 - dispatch_requests配置范围过宽:当前配置匹配所有POST /api/*请求,虽能覆盖登录接口,但精准度不足;且自定义控制器后,默认的Token响应逻辑已失效。
- 未正确获取生成的JWT:调用
sign_in后Devise-JWT已生成Token,但你没有通过正确途径获取。
解决方案
1. 修正Session控制器的Token获取逻辑
调用sign_in后,通过request.env['warden-jwt_auth.token']获取生成的Token,并按规范返回Bearer格式:
class Api::SessionsController < Devise::SessionsController respond_to :json def create user = User.find_by('lower(email) = ?', params[:email].downcase) if user.blank? || !user.valid_password?(params[:password]) render json: { errors: ['邮箱/密码组合无效'] }, status: :unauthorized return end sign_in(:user, user) jwt_token = request.env['warden-jwt_auth.token'] render json: { user: user.as_json(only: [:id, :email, :created_at]), token: "Bearer #{jwt_token}" }, status: :ok end end
2. 优化Devise-JWT配置
将dispatch_requests调整为精准匹配登录接口,避免不必要触发:
config.jwt do |jwt| jwt.secret = ENV['DEVISE_JWT_SECRET_KEY'] jwt.dispatch_requests = [['POST', %r{/api/users/sign_in$}]] jwt.expiration_time = 12.hours.to_i end
3. 确保JWT密钥正确配置
在项目根目录的.env文件中添加安全密钥(可通过rails secret生成):
DEVISE_JWT_SECRET_KEY=your_secure_random_secret_here
4. 验证数据库迁移
确保已运行所有必要的迁移:
rails db:migrate
若未生成迁移文件,先执行:
rails generate devise:install rails generate devise User rails generate devise_jwt:install rails db:migrate
测试验证
- 启动服务器:
rails s - 在Postman中发送POST请求到
http://localhost:3000/api/users/sign_in,请求体为JSON格式:
{ "email": "test@example.com", "password": "your_password" }
- 响应中应返回包含
Bearer {token}的JSON数据,后续请求可将此Token放入Authorization头进行认证。
内容的提问来源于stack exchange,提问作者scrscr87
相关产品推荐
相关产品推荐

