Axios 不发送 cookie
Axios is not sending cookies
我有两个应用程序,server-side 应用程序是用 Laravel 编写的,client-side 应用程序是用 VueJS 编写的。 vue 应用程序使用 laravel 应用程序提供的 api。
授权流程:
用户尝试登录,服务器向客户端发送两个令牌,a) access_token
和 b) refresh_token
成功登录。服务器还以 httpOnly
cookie 的形式向客户端发送刷新令牌,以便在访问令牌过期时,可以使用来自 cookie 的刷新令牌进行刷新。
问题:
当用户登录时,在响应中,服务器发送以下 Set-Cookie
header:
Set-Cookie:
refresh_token=tokenvalue;
expires=Mon, 04-Nov-2019 09:13:28 GMT; Max-Age=604800;
path=/v1/refresh; domain=http://app.test; httponly; samesite=none
这意味着只要有对 /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
端点的请求 header:
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
...以及响应 headers:
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存储机制inner-workings,我也不知道文件系统中是否可以找到httpOnly cookie,但无奈,想知道浏览器是否确实是保存 cookie,我用谷歌搜索发现 cookie 存储在 ~/Library/Application Support/Google/Chrome/Default/Cookies
文件中,这是一个 SQLite 文件。我打开那个文件并搜索我的 cookie ,但它也不在那里(也许 httpOnly cookies 存储在其他地方?)。
现在,我的问题是,如何从 client-side 应用程序检索 cookie?
由于你的 Vue 应用程序和 Laravel (API) 有不同的主机,它不会工作。
您可以重新检查您的服务器响应:
Set-Cookie: refresh_token=tokenvalue; expires=Mon, 04-Nov-2019 09:13:28 GMT; Max-Age=604800; path=/v1/refresh; domain=http://app.test; httponly; samesite=none
它将 cookie 设置为 http://app.test
,而不是 http://localhost:8080
。因此,您的 http://localhost:8080
.
中没有设置 refresh_token
cookie
非常典型的解决方案是:
您需要使用子域,并将您的cookie设置为
domain=.app.test
(整个域)。我的意思是,你需要确定
Laravel和vue在同一个域下
您不需要在 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,
})
}
我有两个应用程序,server-side 应用程序是用 Laravel 编写的,client-side 应用程序是用 VueJS 编写的。 vue 应用程序使用 laravel 应用程序提供的 api。
授权流程:
用户尝试登录,服务器向客户端发送两个令牌,a) access_token
和 b) refresh_token
成功登录。服务器还以 httpOnly
cookie 的形式向客户端发送刷新令牌,以便在访问令牌过期时,可以使用来自 cookie 的刷新令牌进行刷新。
问题:
当用户登录时,在响应中,服务器发送以下 Set-Cookie
header:
Set-Cookie: refresh_token=tokenvalue; expires=Mon, 04-Nov-2019 09:13:28 GMT; Max-Age=604800; path=/v1/refresh; domain=http://app.test; httponly; samesite=none
这意味着只要有对 /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
端点的请求 header:
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
...以及响应 headers:
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存储机制inner-workings,我也不知道文件系统中是否可以找到httpOnly cookie,但无奈,想知道浏览器是否确实是保存 cookie,我用谷歌搜索发现 cookie 存储在 ~/Library/Application Support/Google/Chrome/Default/Cookies
文件中,这是一个 SQLite 文件。我打开那个文件并搜索我的 cookie ,但它也不在那里(也许 httpOnly cookies 存储在其他地方?)。
现在,我的问题是,如何从 client-side 应用程序检索 cookie?
由于你的 Vue 应用程序和 Laravel (API) 有不同的主机,它不会工作。
您可以重新检查您的服务器响应:
Set-Cookie: refresh_token=tokenvalue; expires=Mon, 04-Nov-2019 09:13:28 GMT; Max-Age=604800; path=/v1/refresh; domain=http://app.test; httponly; samesite=none
它将 cookie 设置为 http://app.test
,而不是 http://localhost:8080
。因此,您的 http://localhost:8080
.
refresh_token
cookie
非常典型的解决方案是:
您需要使用子域,并将您的cookie设置为
domain=.app.test
(整个域)。我的意思是,你需要确定 Laravel和vue在同一个域下您不需要在 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,
})
}