介绍
跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 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
而前端地址看个人配置,我的如下:
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')
},
}
$ 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"
},
并且已经给你生成了一个配置文件,内容非常好理解,需要设置的头都给你做好了:
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
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)