简单请求和预检请求

1,065 阅读2分钟

简单请求和预检请求

  • 简单请求:不发送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>

image.png

预检请求的情况
  • 添加了请求头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>

image.png

  • 添加请求头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);
    })
}

image.png

后端并不会真实收到该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") //设置允许请求跨域的网站

image.png