axios 添加登录拦截器后出现 preflight response 错误

前言

为了加强网站安全性,给除了登录注册等特殊页面外的页面路由都加上了访问控制。然后问题就出现了,接口请求没有返回值了!抛了这样一个错误:

1
Request header field 8080:1 Authorization is not allowed by Access-Control-Allow-Headers in preflight response

之前的跨域设置居然不管用了!

挣扎

分析拦截器代码

看看这个拦截器代码究竟做了什么!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// http request 拦截器
axios.interceptors.request.use(
config => {
if (store.state.token) { // 判断是否存在token,如果存在的话,则每个http header都加上token
config.headers.Authorization = `token ${store.state.token}`;
}
return config;
},
err => {
return Promise.reject(err);
});

// http response 拦截器
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
// 返回 401 清除token信息并跳转到登录页面
store.commit(types.LOGOUT);
router.replace({
path: 'login',
query: {redirect: router.currentRoute.fullPath}
})
}
}
return Promise.reject(error.response.data) // 返回接口返回的错误信息
});

嗯!debugger一波找到原因在第五行,添加了一个新的请求头Authorization

1
config.headers.Authorization = `token ${store.state.token}`;

然而,在前端的请求中并没有看到Authorization这个头。。

分析请求

在查看请求详情的时候发现一条重要信息:

options request

options请求过后没有请求接口,那么就证明是服务端拒绝了访问。

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

— 来自 MDN web docs

填坑

解决的办法也很简单,在服务端的跨域拦截器中给OPTIONS方法放行。

1
2
3
4
5
6
7
// 新增一个 Authorization 请求头
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");

// 放行 OPTIONS 请求方法
if (request.getMethod().equals("OPTIONS")) {
response.setStatus(HttpServletResponse.SC_OK);
}

服务端放行OPTIONS方法后,再次请求,就可以看到同一个请求发送了两次。第一条为 options 方法,第二条请求就是 post 或 get 请求啦,并且在 header 中也可以看到 axios 拦截器设置的 Authorization 了。


参考链接

axios 登录拦截器

Access control CORS