跨域请求中携带cookie场景的解决方案实践

金三银四,又到了跳槽的高峰季节,面试中的跨域请求方案在前端八股文中的屡见不鲜,对于跨域场景的了解是非常有必要的,就目前结合的项目实践而言,跨域请求中携带cookie实践过程的了解有助于我们通透了解前端配置中的一些潜在跨域设置问题,这些问题说大不大,可是在日常开发中确是十分常见,可以说在任何前端项目初始化的时候都会遇到,通过demo的验证,我们可以解决以下一些问题:

1.如何在`axios`中配置`withCredentials`;
2.学会设置`Access-Control-Allow-Origin``Access-Control-Allow-Credentials`属性和配置;
3.学会解决跨域请求携带源站cookie的问题;
......
复制代码

我们将通过如下思路来构建解决方案:

  • 首先我们使用express框架搭建第一个服务a(http://localhost:8000),运行在8000端口上;
  • a服务托管index.html(用于在前端页面发送网络请求)文件,相当于在服务a上启用一个同源的前端服务;
  • 在a服务中写一个处理请求的路由,加载index.html页面时,种下cookie(这里种cookie为了在请求B服务时携带上);
  • 再使用express搭建第二个服务b(http://localhost:8001),运行在8001端口上;
  • 在A服务托管的index.html页面去请求B服务,然后把cookie传过去,整个过程就是一个简单的跨域请求携带 cookie 的场景

首先初始化一个node项目,引入 express 框架,先搭建a服务:

// express/app01.js

const express = require('express');
const app = express();

// 通过express服务的IP+ port + /static/index.html 访问前端洁面
app.use('/static', express.static("public"));

// 用户登录成功之后设置 cookie 信息
app.get('/login',(req, res) => {
  res.cookie('user', "kobebryant", {maxAge: 200000, httpOnly: true});
  res.json({code: 0, message: 'login success'});
})

app.get("/user", (req, res) => {
  const user = req.headers.cookie.split("=")[1];
  res.json({code :0, user});
})

app.listen('8000',() => {
  console.log('====== app01 running =======')
})
复制代码

a服务中前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<script src="./axios.min.js"></script>
<body>
  <button id="button01">发送同源请求</button>
  <button id="button02">发送跨域请求</button>
</body>
<script> 
  const button01 = document.querySelector('#button01');
  const button02 = document.querySelector('#button02');

  // 模拟初始化登陆,获取cookie
  axios.get("http://localhost:8000/login", {}).then( (res) =>{
    console.log('res =====>', res);
  })

  // button01 携带cookie同源请求 
  button01.onclick = () => {
    axios.get("http://localhost:8000/user", {}).then( (res) =>{
      console.log('res =====>', res);
    })
  }

  // button02 携带cookie跨域请求 
  button02.onclick = () => {
    axios({
      url: "http://localhost:8001/service",
      method: "get"
    }).then( (res) =>{
      console.log('res =====>', res);
    })
  }
</script>
</html>
复制代码

b服务端代码:

const express = require('express');
const app = express();

app.get("/service", (req, res) => {
  res.json({code :0, msg: '这是跨域请求的响应'});
})

app.listen('8001',() => {
  console.log('====== app02 running =======')
})
复制代码

启动 a,b服务,然后网页中输入url: http://localhost:8000/static/index.html ,此时F12打开控制台: 可以看到发送了一个login请求,并且设置了cookie, 然后我们点击页面上的发送同源请求按钮,可以看到发送了一个user请求,并且已经携带上了cookie,说明同源的请求可以正常携带cookie 信息;接下来刺激的画面来了,我们点击 发送跨域请求 按钮,出现了跨域请求的报错。接下来开始解决跨域携带cookie问题:

    1. 在前端请求的时候设置request对象的属性withCredentials为true

XMLHttpRequest.withCredentials 属性是一个Boolean类型,它指示了是否该使用类似cookies,authorization headers(头部授权)或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control)请求。在同一个站点下使用withCredentials属性是无效的。如果在发送来自其他域的XMLHttpRequest请求之前,未设置withCredentials 为true,那么就不能为它自己的域设置cookie值。而通过设置withCredentials 为true获得的第三方cookies,将会依旧享受同源策略

修改index.html代码中的跨域请求方式:

 // button02 携带cookie跨域请求 
  button02.onclick = () => {
    axios({
      url: "http://localhost:8001/service",
      withCredentials: true,
      method: "get"
    }).then( (res) =>{
      console.log('res =====>', res);
    })
  }
复制代码

这个时候再去发送一个跨域请求,你会发现依旧报错,但是我们仔细看下报错,意思是需要设置header的 Access-Control-Allow-Origin属性。

    1. 在服务端设置Access-Control-Allow-Origin

我们修改b(app02.js)服务的代码:

// 在所有路由前增加,可以拦截所有请求
app.all('*', (req,res,next) => {
  res.header("Access-Control-Allow-Origin", "http://localhost:8000");
  next();
})
复制代码

修改完之后再次发送一个跨域请求,你会发现,又报错了(接近崩溃),但是跟之前报的错不一样了,意思大概就是Access-Control-Allow-Credentials这个属性应该设置为true。

  • 3.在服务端设置Access-Control-Allow-Credentials

再次修改b服务的代码(每次修改后需要重新运行):

// 在所有路由前增加,可以拦截所有请求
app.all('*', (req,res,next) => {
  res.header("Access-Control-Allow-Origin", "http://localhost:8000");
  res.header("Access-Control-Allow-Credentials", true);
  next();
})
复制代码

可以看到,这个跨域请求已经请求成功并且返回数据了!而且也携带了A服务的cookie。总结来说,前端k跨域请求携带cookie认证时,在request对象中配置"withCredentials": true;服务端在response的header中配置"Access-Control-Allow-Origin", "http://xxx" ,在response的header中配置"Access-Control-Allow-Credentials", "true"

猜你喜欢

转载自juejin.im/post/7074217735928381448