介绍
跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 JavaScript 实施的安全限制
同源策略限制了如下行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
 - DOM 和 JS 对象无法获取
 - Ajax 请求发送不出去
 
问题背景
在本地开发时,接口地址一般为 127.0.0.1:8000
$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.jsconst 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') }, }
$ 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:
"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.phpreturn [ '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
proxy: {
  "/api": "http://localhost:8000"
}
生产环境就要 Nginx 反向代理,上文中已经给过配置:
# 前端
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)
