在Rails 5中用Devise和JWT实现用户认证,我遗漏了什么?
我帮你梳理下Rails 5里用Devise+JWT做用户认证时容易遗漏的关键步骤,都是实战中踩过坑的点:
安装适配的JWT Gem
别自己手动写JWT逻辑啦,官方适配的devise-jwt能省超多事。先在Gemfile里加:gem 'devise-jwt'然后跑
bundle install完成安装。完善User模型的Devise配置
确保你的User模型已经包含Devise的核心模块,还要加上JWT相关的配置,比如:class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :jwt_authenticatable, jwt_revocation_strategy: JwtBlacklist end这里的
jwt_revocation_strategy是用来处理token失效的(比如用户登出),后面会说具体实现。生成并配置JWT初始化文件
跑这个命令生成官方的配置模板:rails generate devise:jwt:install生成的
config/initializers/devise_jwt.rb里,记得把密钥改成环境变量(别硬编码!),还能设置token过期时间、触发分发/失效的路由:Devise::JWT.configure do |config| config.secret_key = ENV['DEVISE_JWT_SECRET_KEY'] || Rails.application.credentials.devise_jwt_secret_key config.expiration_time = 3600 # 1小时过期,按需调整 config.dispatch_requests = [['POST', %r{^/login$}]] config.revocation_requests = [['DELETE', %r{^/logout$}]] end实现JWT黑名单策略(可选但必做)
要是没有黑名单,用户登出后token还能继续用,太不安全了。先生成黑名单模型:rails generate model JwtBlacklist jti:string exp:datetime rails db:migrate然后在模型里实现revocation的逻辑:
class JwtBlacklist < ApplicationRecord include Devise::JWT::RevocationStrategies::Blacklist self.table_name = 'jwt_blacklists' end路由配置要到位
除了你写的authenticate_user路由,还要配置好登录、登出和受保护路由:# config/routes.rb devise_for :users, skip: [:session, :password, :registration, :confirmation] post '/login', to: 'authentication#authenticate_user' delete '/logout', to: 'authentication#logout' # 保护API路由示例 namespace :api do namespace :v1 do before_action :authenticate_user! resources :posts end end完善AuthenticationController的方法
你之前的payload方法没写完,补上标准的JWT生成逻辑:private def payload(user) return nil unless user&.id { auth_token: JWT.encode( { user_id: user.id, exp: 1.day.from_now.to_i }, Devise::JWT.secret_key ), user: { id: user.id, email: user.email } } end再加个登出方法,把当前token加入黑名单:
def logout token = request.headers['Authorization']&.split(' ')&.last return render json: { errors: ['No token provided'] }, status: :bad_request unless token jwt_payload = JWT.decode(token, Devise::JWT.secret_key).first JwtBlacklist.create!(jti: jwt_payload['jti'], exp: Time.at(jwt_payload['exp'].to_i)) render json: { message: 'Logged out successfully' }, status: :ok endCORS配置(前后端分离必做)
如果前端是单独的应用,得装rack-cors解决跨域问题:# Gemfile gem 'rack-cors'然后在
config/initializers/cors.rb里配置:Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'your-frontend-domain.com' # 生产环境别用* resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head], expose: ['Authorization'] end end测试认证流程
别光写代码,用curl或Postman测一遍:- 登录:
POST /login带email和password,看是否返回auth_token - 访问受保护路由:带上
Authorization: Bearer <token>,确认能正常返回数据 - 登出:
DELETE /logout带token,之后再用这个token请求应该返回401
- 登录:
内容的提问来源于stack exchange,提问作者adam tropp

