maiy's blog

什么是跨域

2021-05-07

参考
参考

什么是跨域

在开发过程中,我们经常会遇到跨域问题,这里跨域一般是指我们在前端与后端服务不在同源下,前端向后端发送ajax请求,触发浏览器的同源策略,导致请求失败。解决跨域问题的方法有几种,分别是JSONPCORS代理

同源策略

同源策略是浏览器的一个安全策略,限制一个origin的文档去请求另一个源的资源。它能帮助阻隔恶意文档,减少可能被攻击的媒介。同源是指两个URL的protocol、host以及port都相同,则这两个URL是同源。举个同源的例子,URL:http://store.company.com/dir/page.html

JSONP

JSONP解决跨域的思路主要是利用了浏览器对script标签不受同源策略限制,使用该方式的缺点主要是只能进行get请求,并且每次请求都要进行dom操作。下面写个简单的jsonp请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// express 后端代码
const express = require('express');

// 是一个express实例
const app = express();

app.listen(3000, () => {
console.log('启动成功');
})

app.get('/api/jsonp', (req, rep) => {
console.log(req.query)
rep.jsonp({ index: req.query.index + 1 })
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script>
window.callback = function (res) {
// 服务器响应函数
console.log(res)
}
</script>
<!-- 此处调用服务器接口 -->
<script type="text/javascript" src="http://localhost:3000/api/jsonp?index=1&callback=callback"></script>
</body>
</html>

CORS

CORS全称为Cross Origin Resource Sharing(跨域资源共享),这是官方提供解决跨域问题的主要方案。通过配置服务器的响应头来告诉浏览器可以访问该服务器资源。主要的响应首部有以下几个

  • Access-Control-Allow-Origin: host,host为允许访问资源的客户端地址,*代表所有站点均可访问
  • Access-Control-Allow-Methods: POST,这个是服务器所支持的方法
  • Access-Control-Allow-Headers: X-PINGOTHER, Content-Type,该字段是实际请求中被允许的携带的自定义header
  • Access-Control-Max-Age: 86400 该字段是指再86400秒内该请求不需要再发送预检请求
  • Access-Control-Allow-Credentials: true 该字段是允许浏览器能把响应的Cookie等内容返回给发送者,当该字段为true时,Access-Control-Allow-Origin不能设置为*,必须设置为前端服务域名,否则会请求失败

对于可能产生副作用的HTTP请求,浏览器会先使用OPTIONS方法发起一个预检请求,从而获知服务器是否允许该跨域请求。满足以下条件的为简单请求,其余都是复杂请求(即需要先发送OPTIONS请求)

  • 请求方法为:GET、POST、HEAD
  • 请求头只能包含:Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width
  • Content-Type只能为:text/plain、multipart/form-data、application/x-www-form-urlencoded

我自己用express和fetch试了很久,没有试出来为什么不发Options请求,奇怪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// server
app.use(cors({
origin: 'http://10.1.2.13:3002',
methods: ['POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: 'auth, content-type',
credentials: true,
}))

app.listen(3030, () => {
console.log('启动成功');
})

app.post('/api/info', (req, rep) => {
console.log(req.query)
rep.cookie('name', 'peter', {maxAge: 900000, httpOnly: true})
rep.header('Content-Type', 'application/json')
rep.json({ index: 3333 })
})

app.get('/api/info', (req, rep) => {
console.log(req.query)
rep.json({ result: 'success' })
})

app.put('/api/info', (req, rep) => {
console.log(req.query)
rep.json({ result: 'success' })
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function App() {

useEffect(() => {
fetch('http://10.1.2.13:3030/api/info', { method: 'put', body: {}, headers: { 'Content-Type': 'application/json', auth: 'peter-mai' }, credentials: 'include' }).then((res) => {
console.log(res)
})
fetch('http://10.1.2.13:3030/api/info?a=333', { method: 'post', body: {}, headers: { 'Content-Type': 'application/json', auth: 'peter-mai' }, credentials: 'include' }).then((res) => {
console.log(res)
})
}, [])

return (
<div className="App">hello options</div>
);
}

代理

最后一种解决方法就是使用代理,前端请求代理服务器,代理服务器转发到目标服务器。最常用的是nginx服务器代理转发。

使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章