我如何让一个运行在另一个域上的客户端在Rails API中设置JWT到一个cookie中?

9 浏览
0 Comments

我如何让一个运行在另一个域上的客户端在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

0