CORS跨域请求中预检(preflight)

描述

服务端使用 NodeJS Express 搭建包含 JWT 身份验证的 REST Full API, 客户端在获取到 JWT 信息之后的每次 API 请求头中都附带上 JWT 信息,完成身份验证后才能执行 API 操作,否则返回 401 错误。

代码

服务器端(CORS核心部分):

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
...
// Enable CORS from client-side
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials");
res.header("Access-Control-Allow-Credentials", "true");
next();
});

//parse application/json and look for raw text
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.text());
app.use(bodyParser.json({ type: 'application/json' }));

// Routes configuration
apiRoutes(app);

app.listen(port);

------- User -----
//==========================
// User Routes
//==========================
apiRoutes.use('/user', passport.authenticate('jwt', {session: false }), userRoutes);
userRoutes.get('/', user.getUsers);
userRoutes.get('/:id', user.getUser);
userRoutes.post('/', user.postUser);
userRoutes.put('/:id', user.updateUser);
userRoutes.delete('/:id', user.deleteUser);

上面的代码在PostMan 测试中附带 jwt 也是没有任何的问题,成功返回。

但是问题来了,在客户端一直不管使用什么方式请求,都是返回 401

罪魁祸首—预检(Pre-flight)

CORS 中有两种请求:简单请求和非简单请求。于是又翻出来看了下,此时的情况正是属于非简单请求,会发送两次的请求,第一次就是preflight,用于请求验证, 第二次才是用户真正需要发送的请求。

回到代码中,不巧,每次服务端捕捉到的就是这个 preflight 请求,然后做 next,其中就包括Jwt 中间件,而因为请求头中没有 Authorization 这个 header,Jwt 就返回了 401,而这个过程是在 passport 的 JWT 中自动检测的,自己写的 JWT 验证部分甚至都没有执行到!

解决办法

参考 express cors,把请求类型OPTIONS做个简单的过滤就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
// Enable CORS from client-side
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials");
res.header("Access-Control-Allow-Credentials", "true");
if (req.method == "OPTIONS") {
res.send(200); // 这里
}
else {
next();
}
});