问题描述
我在Devise和Devise-JWT上有一个非常典型的Rails后端。当我向端点/users/sign_in
发出原始cURL请求时,可以在标头中看到它正在设置带有令牌的Authorization标头。
当我在React前端上执行相同的请求(位于不同的端口,因此需要跨源配置)时,我在结果中看到的唯一标头是cache-control和content-type。
现在,我已经安装了“ cors” gem。我创建了一个名为config/initializers/cors.rb
的初始化程序,并将此配置放入其中:
Rails.application.config.middleware.insert_before 0,Rack::Cors do
allow do
origins '*'
resource('*',headers: :any,expose: ["Authorization"],methods: :any
)
end
end
但是,当我在React前端发出请求时,没有看到Authorization标头。
我可以看到Rails正在响应302重定向。这是问题的一部分吗?我是否需要配置设备以停止响应302?
我完全不知道可能是什么问题。
如果您观察到下面的屏幕快照,则通过普通格式登录以正常方式登录会给我一个Authorization标头,但是如果我通过AJAX进行登录,则没有标头。
这是前端(AJAX)代码,它没有给我任何授权标头,只有“ cache-control”和“ content-type”:
async function submitLogin(email,password) {
let formData = new FormData();
formData.append("user[email]",email)
formData.append("user[password]",password)
let result = await Axios.post(`${process.env.API_URL}/users/sign_in`,formData,{maxRedirects: 0})
console.log(result)
}
结果:
解决方法
免责声明:这不是您问题的100%答案,但是肯定可以!
如果在您的项目中devise-jwt
不是必需的,我建议您遵循我在my Rails 6 / React template中所做的事情:
- 配置您的项目路由,以覆盖Devise的会话路由,例如I did in my template project
- 重写Devise的
SessionController
,以使其以JSON格式响应并返回JWT:
# frozen_string_literal: true
class SessionsController < Devise::SessionsController
clear_respond_to
respond_to :json
def create
super do |resource|
if user_signed_in?
resource.token = JwtWrapper.encode(user_id: current_user.id)
end
end
end
end
- 在
JwtWrapper
中添加app/helpers/jwt_wrapper.rb
:
# frozen_string_literal: true
module JWTWrapper
extend self
def encode(payload,expiration = nil)
expiration ||= Rails.application.secrets.jwt_expiration_hours
payload = payload.dup
payload['exp'] = expiration.to_i.hours.from_now.to_i
JWT.encode payload,Rails.application.secrets.jwt_secret
end
def decode(token)
decoded_token = JWT.decode token,Rails.application.secrets.jwt_secret
decoded_token.first
rescue StandardError
nil
end
end
现在,您将在响应正文中获得有效的JWT。
奖金
我建议您看看restful-json-api-client package,它可以handle JWT for you在响应中寻找token
属性并将其存储并传递给您的请求。
它也handle JWT renewal automatically!,所以当您的API答复401错误时,如果您配置了续订路径,它将尝试续订JWT。如果成功,它将使用新的JWT重做查询,并且对您的应用程序是透明的;否则,如果无法续订,它将失败返回给您的应用程序,以便您可以执行一些操作,例如将用户重定向到登录名页面。
希望这次我能回答您更多的问题。 ?