• 面试题 跨域请求如何携带cookie?


    前言

    最近在参加面试找工作,陆陆续续的面了两三家。其中面试官问到了一个问题:如何解决跨域问题? 我巴巴拉拉的一顿说,大概了说了四种方法,然后面试官紧接着又问:那跨域请求怎么携带cookie呢?(常规的面试套路,一般都会顺着你的回答往深了问)由于之前的项目都是同源的,不牵涉跨域访问,所以一时没有回答出来,后来研究了下,所以有了这篇文章。

    阅读本文,你将学到:

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

    一. 搭建一个跨域请求的环境

    思路:

    1. 使用express搭建第一个服务A(http://localhost:8000),运行在8000端口上;
    2. A服务托管index.html(用于在前端页面发送网络请求)文件;
    3. A服务中写一个处理请求的路由,加载index.html页面时,种下cookie(这里种cookie为了在请求B服务时携带上);
    4. 使用express搭建第二个服务B(http://localhost:8003),运行在8003端口上;
    5. A服务托管的index.html页面去请求B服务,然后把cookie传过去;

    先看下代码结构,相对比较的简单:

    image.png

    A服务的代码:

    // src/app1.js
    const express = require("express");
    const app = express();
    
    // `index.html` 加载时会请求login接口
    // 设置`cookie`
    app.get("/login", (req, res) => {
      res.cookie("user", "jay", { maxAge: 2000000, httpOnly: true });
      res.json({ code: 0, message: "登录成功" });
    });
    
    // 此接口是检测`cookie`是否设置成功,如果设置成功的话,浏览器会自动携带上`cookie`
    app.get("/user", (req, res) => {
      // req.headers.cookie: user=jay
      const user = req.headers.cookie.split("=")[1];
      res.json({ code: 0, user });
    });
    
    // 托管`index.html`页面
    // 这样的话在`index.html`中发起的请求,默认的源就是`http://localhost:8000`
    // 然后再去请求`http://localhost:8003`就会出现跨域了
    app.use("/static", express.static("public"));
    
    app.listen("8000", () => {
      console.log("app1 running at port 8000");
    });
    复制代码

    index.html的代码:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
      </head>
      <body>
        <h2>this is index.html at port 8000</h2>
        <button id="button">发送同源请求</button>
        <button id="cross-button">发送跨域请求</button>
        <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
        <script>
          const button = document.querySelector("#button");
          const crossButton = document.querySelector("#cross-button");
    
          axios.get("http://localhost:8000/login", {}).then((res) => {
            console.log(res);
          });
          // 发送同域请求
          button.onclick = function () {
            axios.get("http://localhost:8000/user", {}).then((res) => {
              console.log(res);
            });
          };
          // 发送跨域请求
          crossButton.onclick = function () {
            axios({
              method: "get",
              url: "http://localhost:8003/anotherService",
            }).then((res) => {
              console.log(res);
            });
          };
        </script>
      </body>
    </html>
    复制代码

    B服务的代码:

    // src/app2.js
    const express = require("express");
    const app = express();
    
    // 定义一个接口,index.html页面请求这个接口就是跨域(因为端口不同)
    app.get("/anotherService", (req, res) => {
      res.json({ code: 0, msg: "这是8003端口返回的" });
    });
    
    app.listen("8003", () => {
      console.log("app2 running at port 8003");
    });
    复制代码

    这个时候环境基本就搭建好了。

    二、解决跨域携带cookie问题

    首先我们先在A服务的index.html页面中得到一个cookie,运行A服务:

    npm install express -D
    node src/app1.js
    复制代码

    然后打开http://localhost:8000/static/index.html: 没有问题的话,页面长这样:

    image.png

    这个时候F12打开控制台: 可以看到发送了一个login请求,并且设置了cookie,也可以选择浏览器控制台的Application页签,选中cookie,可以看到cookie的信息:

    image.png

    image.png

    然后我们点击页面上的发送同源请求按钮,可以看到发送了一个user请求,并且已经携带上了cookie:

    image.png

    接下来刺激的画面来了,我们点击 发送跨域请求 按钮,出现了跨域请求的报错:

    image.png

    重点: 接下来开始解决跨域携带cookie问题:

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

    什么是withCredentials

    XMLHttpRequest.withCredentials 属性是一个Boolean类型,它指示了是否该使用类似cookies,authorization headers(头部授权)或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control)请求。在同一个站点下使用withCredentials属性是无效的。

    如果在发送来自其他域的XMLHttpRequest请求之前,未设置withCredentials 为true,那么就不能为它自己的域设置cookie值。而通过设置withCredentials 为true获得的第三方cookies,将会依旧享受同源策略,因此不能被通过document.cookie或者从头部相应请求的脚本等访问。

    // 修改跨域请求的代码
    crossButton.onclick = function () {
        axios({
          withCredentials: true, // ++ 新增
          method: "get",
          url: "http://localhost:8003/anotherService",
        }).then((res) => {
          console.log(res);
        });
    };
    复制代码

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

    image.png

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

    我们修改B(app2.js)服务的代码:

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

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

    image.png

    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();
    });
    复制代码

    再发送一个跨域请求:

    image.png

    image.png

    可以看到,这个跨域请求已经请求成功并且返回数据了!而且也携带了A服务的cookie,这个时候已经大功告成了。

    三、总结

    1. 前端请求时在request对象中配置"withCredentials": true
    2. 服务端在responseheader中配置"Access-Control-Allow-Origin", "http://xxx:${port}";
    3. 服务端在responseheader中配置"Access-Control-Allow-Credentials", "true"

    如果看完这篇文章能够帮助到你,请给个赞哦~

    文章中案例的全部代码(点这里)

    来源:https://juejin.cn/post/7066420545327218725
  • 相关阅读:
    【BZOJ 2124】【CodeVS 1283】等差子序列
    【BZOJ 1036】【ZJOI 2008】树的统计Count
    【BZOJ 1901】【ZJU 2112】Dynamic Rankings
    【BZOJ 3924】【ZJOI 2015】幻想乡战略游戏
    【BZOJ 4103】【THUSC 2015】异或运算
    【BZOJ 4513】【SDOI 2016】储能表
    【HDU 3622】Bomb Game
    【BZOJ 3166】【HEOI 2013】Alo
    【BZOJ 3530】【SDOI 2014】数数
    【BZOJ 4567】【SCOI 2016】背单词
  • 原文地址:https://www.cnblogs.com/konglxblog/p/16485710.html
Copyright © 2020-2023  润新知