介绍

跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 JavaScript 实施的安全限制

同源策略限制了如下行为:

  • Cookie、LocalStorage 和 IndexDB 无法读取
  • DOM 和 JS 对象无法获取
  • Ajax 请求发送不出去

问题背景

在本地开发时,接口地址一般为 127.0.0.1:8000

1
2
3
$php artisan serve
Starting Laravel development server: http://127.0.0.1:8000
[Thu Aug 25 10:10:42 2022] PHP 7.4.23 Development Server (http://127.0.0.1:8000) started

而前端地址看个人配置,我的如下:

vue.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const port = process.env.port || process.env.npm_config_port || 9528 // dev port

module.exports = {
publicPath: '/',
outputDir: 'dist',
assetsDir: 'static',
lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false,
devServer: {
port: port,
open: true,
overlay: {
warnings: false,
errors: true
},
before: require('./mock/mock-server.js')
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ npm run dev

> trademark-crm-admin@4.4.0 dev
> vue-cli-service serve

INFO Starting development server...
98% after emitting CopyPlugin

DONE Compiled successfully in 4188ms


App running at:
- Local: http://localhost:9528/
- Network: http://192.168.1.13:9528/

Note that the development build is not optimized.
To create a production build, run npm run build.

这个时候直接配置接口地址来联调时就会出现跨域问题:

Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/member/company/update' from origin 'http://localhost:9528' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:9527' that is not equal to the supplied origin.

后端方案

本来准备自己写个中间件的,但是发现用的 Laravel8 默认引入了一个包 laravel-cors

1
2
3
4
5
6
7
8
9
10
11
12
"require": {
"php": "^7.3|^8.0",
"ext-pdo": "*",
"barryvdh/laravel-ide-helper": "^2.12",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.75",
"laravel/sanctum": "^2.11",
"laravel/tinker": "^2.5",
"predis/predis": "^2.0",
"tymon/jwt-auth": "^1.0"
},

并且已经给你生成了一个配置文件,内容非常好理解,需要设置的头都给你做好了:

cors.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
return [

'paths' => ['api/*'],

'allowed_methods' => ['*'],

'allowed_origins' => ['http://localhost:9528'],

'allowed_origins_patterns' => [],

'allowed_headers' => ['*'],

'exposed_headers' => [],

'max_age' => 0,

'supports_credentials' => true,

];

把前端地址加进去后就完事了

前端方案

如果是纯前端解决跨域问题,可以用 webpack 的 devServer.proxy

1
2
3
proxy: {
"/api": "http://localhost:8000"
}

生产环境就要 Nginx 反向代理,上文中已经给过配置:

1
2
3
4
5
6
7
8
9
10
# 前端
server{
location /api {
proxy_pass http://localhost:81;
}
}
# 后端
server{
listen 81;
}

不是所有的跨域情况下的请求都需要先发送一个 options 请求的。比如一些简单请求是不需要的比如 get 请求,但也不是所有的 get 请求都不会发 options

它的 Content-Type 的值仅限于下列三者之一:

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

并且没有设置如下的 自定义 Header

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (需要注意额外的限制)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width

而且还要注意一点,也不是只有 XMLHttpRequest 或者 fetch 请求才会有跨域问题,使用 drawImage 的 Canvas、Web 字体 、CSSDOM 也都是有这问题的

想了解更多可以阅读 MDN 跨源资源共享(CORS)

跨域