【问题标题】:Axios is not sending cookiesAxios 没有发送 cookie
【发布时间】:2020-02-23 14:29:45
【问题描述】:

我有两个应用程序,一个是用 Laravel 编写的服务器端应用程序,另一个是用 VueJS 编写的客户端应用程序。 vue 应用使用 laravel 应用提供的 api。

身份验证流程:

用户尝试登录,服务器向客户端发送两个令牌,a)access_token 和 b)成功登录后的refresh_token。服务器还会以httpOnly cookie 的形式向客户端发送刷新令牌,这样当访问令牌过期时,可以使用来自 cookie 的刷新令牌进行刷新。

问题:

当用户登录时,服务器会在响应中发送以下Set-Cookie 标头:

设置 Cookie: refresh_token=令牌值; 过期=星期一,2019 年 11 月 4 日 09:13:28 GMT;最大年龄=604800; 路径=/v1/刷新;域=http://app.test;仅限http;同站点=无

这意味着,只要有对 /v1/refresh 端点的请求,我希望将 cookie 发送到服务器。但是,请求中不存在 cookie。 (我在控制器中记录了$request->cookie('refresh_token'),但它记录了null)。

整个令牌刷新机制在 vuex 动作中处理:

export function refreshToken({commit}, payload) {
    return new Promise((resolve, reject) => {
        // axios.defaults.withCredentials = true;

        // here, payload() function just converts the url to:
        // "http://app.test/v1/refresh"

        axios.post(payload('/refresh'), {}, {
            withCredentials: true, transformRequest: [(data, headers) => {
                delete headers.common.Authorization;
                return data;
            }]
        }).then(response => {
            let token = response.data.access_token;
            localStorage.setItem('token', token);
            commit('refreshSuccess', token);
            resolve(token);
        }).catch(err => reject(err));
    });
}

如您所见,我已将 withCredentials 配置设置为 true。我还从服务器发送Access-Control-Allow-Credentials: true。这是我的 cors 中间件:

public function handle($request, Closure $next)
    {
        $whiteList = ['http://localhost:8080'];
        if (isset($request->server()['HTTP_ORIGIN'])) {
            $origin = $request->server()['HTTP_ORIGIN'];
            if (in_array($origin, $whiteList)) {
                header('Access-Control-Allow-Origin: ' . $request->server()['HTTP_ORIGIN']);
                header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
                header('Access-Control-Allow-Headers: Origin, Content-Type, Authorization');
                header('Access-Control-Allow-Credentials: true');
                header('Access-Control-Expose-Headers: Content-Disposition');
            }
        }
        return $next($request);
    }

我不知道我做错了什么。我的 PHP 版本是:7.3.5。这里是/v1/refresh端点的请求头:

Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,bn;q=0.8
Connection: keep-alive
Content-Length: 15
Content-Type: application/x-www-form-urlencoded
Host: app.test
Origin: http://localhost:8080
Referer: http://localhost:8080/products
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36

...以及响应标头:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, Content-Type, Authorization
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Expose-Headers: Content-Disposition
Cache-Control: no-cache, private
Connection: keep-alive
Content-Type: application/json
Date: Mon, 28 Oct 2019 09:40:31 GMT
Server: nginx/1.15.5
Transfer-Encoding: chunked
X-Powered-By: PHP/7.3.5
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59

我不知道浏览器的cookie存储机制的内部工作原理,我也不知道是否可以在文件系统中找到httpOnly cookie,但绝望地想知道浏览器是否确实保存了cookie,我用谷歌搜索,发现 cookie 存储在~/Library/Application Support/Google/Chrome/Default/Cookies 文件中,这是一个 SQLite 文件。我打开了那个文件并搜索了我的 cookie ????,但它也不存在(也许 httpOnly cookie 存储在其他地方?)。

现在,我的问题是,如何从客户端应用中检索 cookie?

【问题讨论】:

  • 您的主机到底是什么?是localhost:8080 还是app.test
  • 感谢您的回复。 localhost:8080 是提供我的 vue 应用程序的地方。 app.test 是我的 laravel 应用程序的来源。它们是两个不同的应用程序/域/来源。这是一个非常典型的 SPA 设置,它与 laravel 应用程序完全隔离。
  • 那么,vue app 和 api 有不同的主机,对吧?当然它不会起作用,因为你的 laravel 将 cookie 设置为 app.test,而不是 localhost:8080。看一下域名path=/v1/refresh; domain=http://app.test; httponly; samesite=none

标签: laravel vue.js cookies axios


【解决方案1】:

由于你的 Vue App 和 Laravel (API) 有不同的 HOST,它不会工作。

您可以重新检查您的服务器响应:

Set-Cookie: refresh_token=tokenvalue;过期=星期一,2019 年 11 月 4 日 09:13:28 GMT;最大年龄=604800;路径=/v1/刷新;域=http://app.test;仅限http;同站点=无

它将 cookie 设置为 http://app.test,而不是 http://localhost:8080。因此,您的http://localhost:8080 中没有设置refresh_token cookie。

非常典型的解决方案是:

  1. 您需要使用子域,并让您的 cookie 设置为 domain=.app.test(整个域)。我的意思是,你需要确保 Laravel 和 Vue 在同一个域下。

  2. 你不需要在你的 Laravel 应用程序中再次从 cookie 中获取 refresh_token。首先,您只需要将您从 API 获得的 refresh_token 保存到 Vue 应用程序的 localStorage 或 cookie 中。然后,只需通过表单(表单数据)发送您的refresh_token。最后,通过$request->get('refresh_token') 获取您的refresh_token

这是一个例子,只是为了说明我对第二种解决方案的意思。

让我们假设(通常)http://app.test/api/login 会响应:

{
    "token_type": "Bearer",
    "expires_in": 31622399,
    "access_token": "xxx",
    "refresh_token": "xxx"
}
import Cookies from 'js-cookie'

async login() {
    const { data } = await axios.post('http://app.test/api/login', {
        email: 'hi@app.test',
        password: 'secret',
    })

    const refreshToken = data.refresh_token

    Cookies.set('refresh_token', refreshToken)
},
async refreshToken() {
    const refreshToken = Cookies.get('refresh_token')

    const response = await axios.post('http://app.test/api/refresh-token', {
        refresh_token: refreshToken,
    })
}

【讨论】:

  • 将域更改为localhost:8080,清除缓存、cookie 和所有内容。又试了一次。仍然无法检索 cookie。
  • changed domain to localhost:8080 是什么意思?你不能将 cookie 从 Laravel app.test 设置为 http://localhost:8080Here is why.
  • 我的意思是我将 domain=app.test 更改为 localhost:8080,正如你所提到的。
  • 我正在处理您的第一点。但是您建议通过表单发送刷新令牌的第二点是无法完成的。因为这个 cookie 是 httpOnly cookie。无法从$request->get()检索到
  • 第二点,这就是问题所在。你不需要在 Laravel 中再次将 refresh_token 设置为 cookie。您只需要保存您的 refresh_token 即可在您的 Vue 应用程序中对 localStorage 或 cookie 进行身份验证。
猜你喜欢
  • 2019-03-12
  • 2019-11-06
  • 2021-11-03
  • 2023-03-28
  • 1970-01-01
  • 2019-01-04
  • 2021-10-30
  • 1970-01-01
  • 2011-05-18
相关资源
最近更新 更多