Rails API控制器无法获取已解码JWT Token问题求助
问题:Rails API中JWT解码成功但控制器无法获取内容
我开发了一个用JWT实现登录的Rails API,登录流程正常,Token已存入本地存储,但在通过Token解码做控制器动作授权时遇到问题。多个控制器用authorize_request作为前置动作,定义在ApplicationController中:
class ApplicationController < ActionController::API def not_found render json: { error: 'not_found' } end def authorize_request header = request.headers['Authorization'] header = header.split(' ').last if header puts "header is #{header}" begin @decoded = JsonWebToken.decode(header) puts "decoded header is #{@decoded}" @current_user = User.find(@decoded[:user_id]) puts "current_user is #{@current_user}" rescue ActiveRecord::RecordNotFound => e render json: { errors: e.message }, status: :unauthorized rescue JWT::DecodeError => e render json: { errors: e.message }, status: :unauthorized end end end
JsonWebToken类的decode方法代码:
class JsonWebToken SECRET_KEY = Rails.application.secrets.secret_key_base def self.encode(payload, exp = 24.hours.from_now) payload[:exp] = exp.to_i JWT.encode(payload, SECRET_KEY) end def self.decode(token) puts("decoding token: #{token}") decoded = JWT.decode(token, SECRET_KEY) puts("decoded is #{decoded}") HashWithIndifferentAccess.new decoded end end
日志显示Token在JWT类中解码成功,但控制器里@decoded是空的:
header is eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNjZiZmQyMDAtZDExMy00NjdlLTkwMmEtMDRjMmI5YjE2ZmUwIiwiZXhwIjoxNjgwNzAyMzExfQ.x95ikz4QeWAdgm2rak_L6eUOqhILsRIepvALGieAwx8 decoding token: eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiNjZiZmQyMDAtZDExMy00NjdlLTkwMmEtMDRjMmI5YjE2ZmUwIiwiZXhwIjoxNjgwNzAyMzExfQ.x95ikz4QeWAdgm2rak_L6eUOqhILsRIepvALGieAwx8 decoded is [{"user_id"=>"66bfd200-d113-467e-902a-04c2b9b16fe0", "exp"=>1680702311}, {"alg"=>"HS256"}] decoded header is {}
原因分析
从日志能看到,JWT.decode返回的是一个数组,第一个元素是包含用户信息的payload,第二个是JWT的头部信息。但你在JsonWebToken.decode方法里,直接把整个数组传给了HashWithIndifferentAccess.new,这会把数组的索引(0、1)作为键,对应的值是数组元素,但你需要的是payload部分,也就是数组的第一个元素。当你在控制器里访问@decoded[:user_id]时,这个键不存在,所以获取不到值,打印出来的@decoded看起来是空的(实际是包含索引键的Hash,但你没用到)。
解决方案
修改JsonWebToken的decode方法,只取数组的第一个元素(payload)来转成HashWithIndifferentAccess:
class JsonWebToken SECRET_KEY = Rails.application.secrets.secret_key_base def self.encode(payload, exp = 24.hours.from_now) payload[:exp] = exp.to_i JWT.encode(payload, SECRET_KEY) end def self.decode(token) puts("decoding token: #{token}") decoded = JWT.decode(token, SECRET_KEY) puts("decoded is #{decoded}") # 取数组第一个元素(payload)转成HashWithIndifferentAccess HashWithIndifferentAccess.new decoded[0] end end
这样修改后,@decoded就会包含user_id和exp字段,控制器里就能正常通过@decoded[:user_id]找到对应的用户了。
内容的提问来源于stack exchange,提问作者user2799827
相关产品推荐
相关产品推荐

