简单请求和预检请求
- 简单请求:不发送options进行预检,去判断接口是否存在跨域问题,如果存在跨域,那么浏览器拦截返回数据,实际上后端是接收到这个请求,并且进行处理了,只是浏览器的安全策略将其拦截了。
- 预检请求:发送真实请求前,先发送options请求去判断接口是否存在跨域问题,如果发生了跨域,那么真实请求则不会发送到后端进行处理。
那么这里,抛出一个问题,什么时候浏览器会进行预检请求?
1、请求方法
- GET
- POST
- HEAD
2、请求头
- Accept
- Accept-Language
- Content-Language
简单理解,即不存在自身携带的请求头,例如:token等
3、请求数据格式
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
- 如果同时满足上面三种请求,则请求被判定为简单请求,反之,有一项不满足,则为预检请求。
例子
简单请求的情况
后端代码跨域设置:
res.setHeader("Access-Control-Allow-Headers", "name, content-type")
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:5501")
// 设置为true Access-Control-Allow-Origin和Access-Control-Allow-Headers不能设置为*
res.setHeader("Access-Control-Allow-Credentials", true) // 表示允许跨域请求携带cookie
session设置:
app.use(session({
secret: "xiaoyuchongchongchong",
resave: true,
saveUninitialized: false,
name: "sessionid",
cookie: { path: "/", httpOnly: true, secure: false, maxAge: 1000 * 60 * 60 } //cookie默认配置 maxAge单位ms
}))
设置前端登录接口如下:
router.get("/login", (req, res, next) => {
if (req.query.loginId === "admin" && req.query.loginPwd === "123456") {
req.session.name = "xiaoyu"
res.send("登录成功")
} else {
res.send("登录失败")
}
})
前端使用axios模拟发包:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简单请求和预检请求</title>
</head>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.7.2/axios.min.js"></script>
<button onclick="login()">Login</button>
<script>
// 同源请求会自动携带cookie
const instance = axios.create({
withCredentials: true, // 允许跨域请求携带cookie
});
function login() {
instance.get("http://127.0.0.1:1122/login?loginId=admin&loginPwd=123456").then(res => {
console.log(res);
})
}
</script>
</body>
</html>
预检请求的情况
- 添加了请求头name,不符合简单请求的规则
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简单请求和预检请求</title>
</head>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.7.2/axios.min.js"></script>
<button onclick="login()">Login</button>
<script>
// 同源请求会自动携带cookie
const instance = axios.create({
withCredentials: true, // 允许跨域请求携带cookie
});
function login() {
instance.get("http://127.0.0.1:1122/login?loginId=admin&loginPwd=123456", { headers: { "name": "xx" }}
).then(res => {
console.log(res);
})
}
</script>
</body>
</html>
- 添加请求头age
不符合res.setHeader("Access-Control-Allow-Headers", "name, content-type")
// 同源请求会自动携带cookie
const instance = axios.create({
withCredentials: true, // 允许跨域请求携带cookie
});
function login() {
instance.get("http://127.0.0.1:1122/login?loginId=admin&loginPwd=123456", { headers: { "name": "xx", "age": 18 } }
).then(res => {
console.log(res);
})
}
后端并不会真实收到该get请求,因为预检请求检查出跨域了。
再看一种情况,虽然跨域,由于是简单请求,浏览器不会发送预检请求,后端还是接收到了请求并做了相应的处理,只是返回数据被浏览器出于安全策略拦截了【由于http://127.0.0.1:5502和http://127.0.0.1:5501是同站并且协议一样,还是会携带cookie,这种方式常见于csrf攻击】
res.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:5502") //设置允许请求跨域的网站