一、前言

前后端分离的开发模式下,跨域问题总是很常见的。

二、浏览器同源策略

同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说 Web 是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

它的核心就在于它认为自任何站点装载的信赖内容是不安全的。当被浏览器半信半疑的脚本运行在沙箱时,它们应该只被允许访问来自同一站点的资源,而不是那些来自其它站点可能怀有恶意的资源。

所谓同源是指:域名、协议、端口相同。

三、解决跨域的方式

解决跨域的方式有很多:JSONP、CORS、服务器代理、document.domain、window.name、location.hash、postMessage

四、JSONP 处理跨域

1. JSONP 处理跨域的原理

由于 script 标签不受浏览器同源策略的影响,允许跨域引用资源。因此可以通过动态创建 script 标签,然后利用 src 属性进行跨域,这也就是 JSONP 跨域的基本原理。

2. 优缺点

优点:

  • 使用简便,没有兼容性问题。

    缺点:

  • 只支持 GET 请求。

  • 前后端需要配合。
  • 由于是从其它域中加载代码执行,因此如果其他域不安全,很可能会在响应中夹带一些恶意代码。

3. 示例代码

后端 express

  1. const express = require('express')
  2. const app = express()
  3. app.get('/', (req, res) => {
  4. res.jsonp({
  5. code: 0,
  6. msg: 'OK'
  7. })
  8. })
  9. app.listen(3000)

前端

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <h1>jsonp</h1>
  10. <script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
  11. <script>
  12. $(function () {
  13. $.ajax({
  14. url: 'http://localhost:3000',
  15. type: 'GET',
  16. dataType: 'jsonp',
  17. success: function (res) {
  18. console.log(res)
  19. }
  20. })
  21. })
  22. </script>
  23. </body>
  24. </html>

五、CORS (跨域资源共享) 处理跨域

1. 优缺点

优点:

  • CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。
  • 支持所有类型的 HTTP 请求。

    缺点:

  • 存在兼容性问题,特别是 IE10 以下的浏览器。

  • 第一次发送非简单请求时会多一次请求。

2. 示例代码

后端 express

  1. const express = require('express')
  2. const app = express()
  3. app.use((req, res, next) => {
  4. // 设置响应头
  5. res.set({
  6. // 控制允许的请求来源
  7. 'Access-Control-Allow-Origin': '*',
  8. // 控制允许的自定义请求头
  9. 'Access-Control-Allow-Headers': 'abc,efg',
  10. // 控制允许的请求方式
  11. 'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE'
  12. })
  13. next()
  14. })
  15. app.get('/', (req, res) => {
  16. res.json({
  17. code: 0,
  18. msg: 'OK'
  19. })
  20. })
  21. app.listen(3000)

前端

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <h1>cors</h1>
  10. <script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
  11. <script>
  12. $(function () {
  13. $.ajax({
  14. url: 'http://localhost:3000',
  15. type: 'GET',
  16. success: function (res) {
  17. console.log(res)
  18. }
  19. })
  20. })
  21. </script>
  22. </body>
  23. </html>

六、服务器代理 处理跨域

1. 服务器代理 处理跨域的原理

浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所要域的资源再返回给客户端。

2. 图解

JSONP、CORS 与 代理解决跨域 - 图1

3. 示例代码 - 依赖 http-proxy-middleware

  1. 目标API接口地址:http://m.maoyan.com/ajax/movieOnInfoList
  2. 目标服务器:http://m.maoyan.com
  3. 代理服务器:http://localhost:3000
  4. Web服务器:http://localhost:8080

代理服务器代码

  1. const express = require('express')
  2. const { createProxyMiddleware } = require('http-proxy-middleware');
  3. const app = express()
  4. app.use((req, res, next) => {
  5. // 设置响应头
  6. res.set({
  7. // 控制允许的请求来源
  8. 'Access-Control-Allow-Origin': '*',
  9. // 控制允许的自定义请求头
  10. 'Access-Control-Allow-Headers': 'abc,efg',
  11. // 控制允许的请求方式
  12. 'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE'
  13. })
  14. next()
  15. })
  16. app.use('/api', createProxyMiddleware({
  17. // 目标服务器地址
  18. target: 'http://m.maoyan.com',
  19. // 虚拟托管站点所需, 一般直接设置为true就好
  20. changeOrigin: true,
  21. /**
  22. * 路径重写
  23. *
  24. * 当我们通过代理访问目标接口的流程大致如下
  25. * http://localhost:8080 -> 请求 ->
  26. * http://localhost:3000/api/ajax/movieOnInfoList -> 代理到 ->
  27. * http://m.maoyan.com/api/ajax/movieOnInfoList
  28. * 这时真实的目标接口地址是没有 /api 这个前缀的。所以会导致请求失败 404 等问题
  29. *
  30. * 设置如下的路径重写规则之后,流程如下
  31. * http://localhost:8080 -> 请求 ->
  32. * http://localhost:3000/api/ajax/movieOnInfoList -> 代理到 ->
  33. * http://m.maoyan.com/ajax/movieOnInfoList
  34. */
  35. pathRewrite: {
  36. '^/api': ''
  37. }
  38. }))
  39. app.listen(3000)

前端

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <h1>服务器代理</h1>
  10. <script src="https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.min.js"></script>
  11. <script>
  12. $(function () {
  13. $.ajax({
  14. url: 'http://localhost:3000/api/ajax/movieOnInfoList',
  15. type: 'GET',
  16. success: function (res) {
  17. console.log(res)
  18. }
  19. })
  20. })
  21. </script>
  22. </body>
  23. </html>

参考链接