# 跨域问题

网络攻防包括: CSRF(跨站请求伪造攻击),XSS(跨站脚步攻击),SQL 注入,cookie 拦截修改

# 表现形式

1,接口 403
2,cor 跨域

# 产生问题的原因

# 网络域和浏览器的同源策略

# 什么是同源策略

1,协议相同
2,域名相同
3,端口相同
三者同时成立叫同源,
从域名 A 下的一个页面(一般是通过 ajax 请求)获取域名 B 下的一个资源,是不被浏览器容许的。

# 跨域有四个点要满足:

1,A 和 B 不同源
2,同源策略是浏览器做的限制(所以 APP 永远不会跨域)
3,从 H5 发送的请求,经过浏览器
4,跨域的意思是:浏览器还是会发送请求,但浏览器会拦截响应内容,如果发现响应 header 中有 "Access-Control-Allow-Origin"设置的容许访问源没有包含当前源,则拒绝将数据返回给当前源(可以设置值为 * )
ep: <img> <script> <style><link> 等标签里有 src 是不做同源限制的

# 绕过同源策略的多种方式

# 1 服务器做设置,即响应头添加"Access-Control-Allow-Origin",指定容许访问的源

@PostMapping "/account/login";
@ResponseBody
public ResulttModel<String> login
  HttpServietResponse response,
  HttpServietRequest request,
  @RequestParam String useId,
  @RequesstParam String password
  // response.setHeader("Access-Control-Allow-Origin", "*");
  return this.accountService.login response,userId, password;

node 的写法

// servernode.js  中间件
const http = require("http");
// 第一步 接受客户端请求
const server = http.createServer((request, response) => {
  // 代理服务器,直接和浏览器交互,设置CORS的首部字段
  response.writeHead(200, {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "*",
    "Access-Control-Allow-Headers": "Content-Type",
  });
  // 第二步 将请求转发给服务器
  const proxyRequest = http
    .request(
      {
        host: "127.0.0.1",
        port: 4000,
        url: "/",
        method: request.method,
        headers: request.headers,
      },
      (serverResponse) => {
        // 第三步 服务器收到响应
        var body = "";
        serverResponse.on("data", (chunk) => {
          body += chunk;
        });
        serverResponse.on("end", () => {
          console.log("the data is" + body);
          // 第四步 将响应结果转发给浏览器
          response.end(body);
        });
      }
    )
    .end();
});
server.listen(3000, () => {
  console.log("The proxyServer is running at http://localhost:3000");
});
// servver2.js
const http = require("http");
const data = { title: "fontend", password: "123456" };
const server = http.createServer((request, response) => {
  if (request.url === "/") {
    response.end(JSON.stringify(data));
  }
});
server.listen(4000, () => {
  console.log("The server is running at http://localhost:4000");
});

# 2 document.domain

var w = window.open("http://www.qq.com");
w.document;
// Uncaught demoxception: Blocked a frame with origin "http://id.qq.com" from accessing a cross-origin frame.

// 设置document.domain
document.domain = "qq.com";
w.document;
// success

# 3 利用<script>不受限制的设定,通过把不同源的请求伪装成一个脚本请求,服务器端返回数据后,脚本会自动回调,这就是所谓的 JSONP 跨域。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body>
    <script>
      function jsonp({ url, params, callback }) {
        return new Promise((resolve, reject) => {
          let script = document.createElement("script");
          window[callback] = function(data) {
            resolve(data);
            document.body.removeChild(script);
          };
          params = { ...params, callback };
          let arrs = [];
          for (let key in params) {
            arrs.push(`${key}=${params[key]}`);
          }
          script.src = `${url}?${arrs.join("&")}`;
          document.body.appendChild(script);
        });
      }
      jsonp({
        url: "http://localhost:3000/say",
        params: { wd: "iloveyou" },
        callback: "show",
      }).then((data) => {
        console.log(data);
      });
    </script>
  </body>
</html>
// node服务
let express = require("express");
let app = express();
app.get("/say", function(req, res) {
  let { wd, callback } = req.query;
  console.log(wd);
  console.log(callback);
  res.end(`${callback}("我不爱你")`);
});
app.listen(3000);

# 4 服务器代理,正向代理与反向代理

正向代理:与页面同源的服务器代你向不同源的服务器请求数据并转发到页面 反向代理:使用 nginx 地址映射(a 请求 b,a 的服务器上虚拟出服务 c,其实 c 只是表面上和 a 同源,真正的映射到到 b,a 实际上是向 b 请求)

# 5 CORS 跨域资源共享(Cross-origin resource sharing)

实现 CORS 的关键在于服务器,只要服务器实现 CORS 接口,就可以跨域通信。 需要注意的是,CORS 解决跨域的话,Content-Type 的值不属于下列之一:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain
...
<script>
  let xhr = new XMLHttpRequest();
  document.cookie = "name=xiaomin"; // cookie 不能跨域
  xhr.withCredentials = true; // 前端设置是否带cookie
  xhr.open("PUT", "http://localhost:4000/getData", true);
  xhr.setRequestHeader("name", "xiaomin");
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
        console.log(xhr.response);
        console.log(xhr.getResponseHeader("name"));
      }
    }
  };
  xhr.send();
</script>
...
// node  js server1.js
// 设置白名单
let express = require("express");
let app = express();
app.use(express.static(__dirname));
app.listen(3000);


// node js  server2.js
let express = require("express")
let app = express();
let whiteList=["http://localhost:3000"] // 设置白名单
app.use(function(req, res, next){
  let origin = req.headers.origin;
  if(whiteList.includes(origin)){
    // 设置哪个源可以访问我
    res.setHeader("Access-Control-Allow-Origin", origin);
    // 允许哪个头访问我
    res.setHeader("Access-Control-Allow-Headers", "name");
    // 允许哪个方法访问我  为避免多次"预检"请求,返回所有支持的方法
    res.setHeader("Access-Control-Allow-Methods", "PUT,GET");
    // 允许携带cookie
    res.setHeader("Access-Control-Allow-Credentials", true);
    // 预检的存活时间 单位是秒 容许缓存该条回应xxs,在此期间,不用发出另一条预检请求
    res.setHeader("Access-Control-Max-Age", 6);
    // 允许返回头
    res.setHeader("Access-Control-Expose-Headers", "name");
    if(req.method === "OPTIONS"){
      res.end();
    }
  }
  next();
})
app.put("/getData", function(req, res){
  console.log(req.headers);
  res.setHeader("name", "jw");
  res.end("我不爱你瞭")
})
app.get("/getData", function(req, res){
  console.log(req.header);
  res.end("dodo 不爱");
})
app.use(express.static(__dirname))
app.listen(4000)

# 6 window.postMessage()

<!-- A窗口<http://localhost:3000/a.html>  -->
<!DOCTYPE html>
<html charset="UTF-8">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>DOCUMENT</title>
  </head>
  <body>
    <iframe
      src="http://localhost:4000/b.html"
      frameborder="0"
      id="frame"
      onload="load()"
    ></iframe>
    <script>
      function load() {
        let frame = document.getElementById("frame");
        frame.contentWindow.postMessage("iloveyou", "http://localhost:4000");
        window.addEventListener("message", receiveMessage, false);
      }
      // event.source 是通过window.open打开的弹出页面
      // event.data 是popup发送给当前页面的消息
      function receiveMessage(event) {
        if (event.origin !== "http://localhost:4000") return;
        console.log(event.data);
      }
    </script>
  </body>
</html>
<!-- // b窗口<http://localhost:3000/b.html>  -->
<!DOCTYPE html>
<html charset="UTF-8">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>DOCUMENT</title>
  </head>
  <body>
    <script>
      window.onmessage = function(e) {
        console.log(e.data);
        e.source.postMessage("idontloveyou", e.origin);
      };
    </script>
  </body>
</html>

# 7 window.name

a 和 b 是同域的 http://localhost:3000 c 是独立的 http://localhost:4000 a 获取 c 的数据 a 先引用 c c 把值放到 window.name,把 a 引用的地址改到 b

<body>
  <iframe
    src="http://localhost:4000/c.html"
    frameborder="0"
    onload="load()"
    id="iframe"
  ></iframe>
  <script>
    let first = true;
    function load() {
      if (first) {
        let iframe = document.getElementById("iframe");
        iframe.src = "http://localhoset:3000/b.html";
        first = false;
      } else {
        console.log(iframe.contentWindow.name);
      }
    }
  </script>
</body>
// b.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
  </head>
  <body></body>
</html>
// c.html
<body>
  window.name="我不爱你"
</body>

# 8 window.onhashchange

路径后的 hash 值可以用来通信 目的 a 想访问 c a 给 c 转一个 hash c 收到 hash 后 c 把 hash 传给 b b 将结果放到 a 的 hash 里

<!-- // a.html -->
<script>
  window.onhashchange = function() {
    console.log(location.hash);
  };
</script>
<!-- // b.html -->
<script>
  window.parent.parent.location.hash = location.hash);
</script>
<!-- // c.html -->
<script>
  console.log(location.hash);
  let iframe = document.createElement("iframe");
  iframe.src = "http://localhost:3000/b.html#idonotloveyou";
  document.body.appendChild(iframe);
</script>

# 9 websocket

let express = require("express");
let app = express();
let WebSocket = require("ws");
let wss = new WebSocket.Server({ port: 3000 });
wss.on("connection", function(ws) {
  ws.on("message", function(data) {
    console.log(data);
    ws.send("我不爱你");
  });
});
<!-- // a.html -->
<script>
  let socket = new WebSocket("ws://localhost:3000");
  socket.onopen = function() {
    socket.send("iloveyoou");
  };
  socket.onmessage = function(e) {
    console.log(e.data);
  };
</script>