我如何让一个运行在另一个域上的客户端在Rails API中设置JWT到一个cookie中?
我如何让一个运行在另一个域上的客户端在Rails API中设置JWT到一个cookie中?
我是一个长期潜水者,第一次在这里发帖。
有很多关于JWT和如何存储和发送它们的好的指南和资源。但是当涉及到在一个运行在Node服务器上的ReactJS/Flux应用和一个完全独立的Rails API之间安全地存储和发送JWT时,我遇到了一个难题。
大多数指南告诉你只需将JWT存储在本地存储中,并在每次发送AJAX请求时提取它并在头部中传递它。https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/然而,它警告说,由于本地存储不安全,恶意人员可能会访问该令牌。它建议将其存储在cookie中,并让Web浏览器在每个请求中传递它。
对我来说,这听起来不错,因为据我所知,cookie会方便地随每个请求一起发送。这意味着我可以从我的ReactJS应用向我的Rails API发送AJAX请求,然后API会提取它,检查它,然后进行相关操作。
我遇到的问题是,尽管Rails API(在localhost:3000上运行)返回一个Set-Cookie头并将其发送回ReactJS/Node应用(在localhost:8080上运行),但我的Node应用程序没有从响应中设置cookie。
以下是我的Rails API端登录控制器动作:
class V1::SessionsController < ApplicationController def create user = User.where(email: params[:user][:email]).first! if user && user.authenticate(params[:user][:password]) token = issue_new_token_for(user) # 我也试过这种方法。 # cookies[:access_token] = { # :value => token, # :expires => 3.days.from_now, # :domain => 'https://localhost:8080' # } response.headers['Set-Cookie'] = "access_token=#{token}" render json: { user: { id: user.id, email: user.email }, token: token }, status: 200 else render json: { errors: '用户名或密码不匹配' }, status: 422 end end end
它的要点是,它接收一个电子邮件和密码,查找用户,并且如果信息符合要求,则生成JWT。
以下是从我的Node应用调用它的AJAX请求:
$.ajax({ url: 'http://localhost:3000/v1/login', method: 'post', dataType: 'json', data: { user: { email: data.email, password: data.password }, callback: '' //required to get around ajax CORS }, success: function(response){ console.log(response); }, error: function(response) { console.log(response); } })
检查来自Rails API的响应时,可以看到它具有一个Set-Cookie头,值为access_token=jwt.token.here
截图:
Chrome Dev Tools Inspector Screenshot
然而,localhost:8080没有显示任何设置的cookie,并且我的Node/React应用的后续AJAX调用没有发送任何cookie。
我的问题是,我对哪些部分有误解?在这种情况下,我需要做什么才能使存储JWT在cookie中起作用?
一个后续问题是:假设在cookie中存储JWT不可行,那么在本地存储中存储JWT可能存在哪些安全风险(假设我没有将任何敏感信息放入JWT,并且它们都在一定时间内过期)?
*这可能是我有的一个基本误解,请纠正我是否理解错误。
可能感兴趣的附注:
- 我的Rails API已经设置了CORS,只允许来自localhost:8080的流量在开发环境中。
- 在生产环境中,Node/React应用可能运行在一个主域名(example.com)上,而Rails API可能运行在一个子域名(api.example.com)上,但我还没有研究到那个程度。
- 我的JWT中没有任何敏感信息,因此本地存储是一个选择,但我想知道为什么我的设置不能使用cookie。
更新 elithrar 提交了一个有效的答案:
我需要修改我的AJAX请求,并添加xhrFields和crossDomain,以及告诉jQuery支持跨域:
$.support.cors = true; $.ajax({ url: 'http://localhost:3000/v1/login', method: 'post', dataType: 'json', xhrFields: { withCredentials: true }, crossDomain: true, data: { user: { email: data.email, password: data.password } }, success: function(response){ console.log(response); }, error: function(response) { console.log(response); } })
并且我在我的Rails API的Rack Cors配置中添加了credentials: true和expose: true(*仅适用于我的开发环境):
config.middleware.insert_before 0, 'Rack::Cors' do allow do origins '*' resource '*', :headers => :any, :methods => [:get, :post, :put, :path, :options], credentials: true, expose: true end
end