同源策略会隔离不同源的 DOM、页面数据和网络通信,进而实现 Web 页面的安全性。但很多时候我们需要跨域资源共享,进行安全地跨域操作,下面我们讲解目前主要采用的几种解决跨域的方式
jsonp
跨域CORS
)代理
跨域通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。
前端代码示例:index.html
doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>jsonptitle>
head>
<body>
<h1>通过jsonp跨域h1>
<script>
const script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://localhost:8080/getUserInfo?userId=1&callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
console.log(res);
}
script>
body>
html>
执行 npx http-server --port=9000
启动服务
后端 node.js
代码示例:server.js
const http = require('http')
const url = require('url')
const server = http.createServer((request, response) => {
const urlObj = url.parse(request.url, true)
console.log(urlObj);
switch (urlObj.pathname) {
case '/getUserInfo':
const data = {
name: '张三',
id: '1',
sex: '男',
}
response.end(`${urlObj.query.callback}(${JSON.stringify(data)})`)
break
default:
response.end('404.')
break
}
})
server.listen(8080, () => {
console.log('localhost:8080');
})
执行 node server.js
可以看到 jsonp
跨域成功
普通跨域请求:只服务端设置
Access-Control-Allow-Origin
即可,前端无须设置,若要带cookie
请求:前后端都需要设置。需注意的是:由于同源策略的限制,所读取的
cookie
为跨域请求接口所在域的cookie
,而非当前页。目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS)),
CORS
也已经成为主流的跨域解决方案。
前端代码示例:index.html
doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>跨域资源共享CORStitle>
head>
<body>
<h1>跨域资源共享CORSh1>
<script>
fetch('http://localhost:8080/getUserInfo', {
credentials: 'include', // 前端跨域携带cookie
})
.then(response => response.json())
.then(res => {
console.log(res);
})
script>
body>
html>
执行 npx http-server --port=9000
启动服务
后端 node.js
代码示例:server.js
若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,说明没设成功。
const http = require('http')
const url = require('url')
const server = http.createServer((request, response) => {
const urlObj = url.parse(request.url, true)
let data = ''
request.on('data', (chunk) => {
data += chunk
})
request.on('end', () => {
switch (urlObj.pathname) {
case '/getUserInfo':
const data = {
name: '张三',
id: '1',
sex: '男',
}
response.writeHead(200, {
'Access-Control-Allow-Credentials': 'true', // 后端允许发送Cookie
'Access-Control-Allow-Origin': 'http://127.0.0.1:9000', // 允许访问的域(协议+域名+端口)
/*
* 此处设置的cookie还是 localhost:8080 的而非 127.0.0.1:9000,因为后端也不能跨域写cookie(nginx反向代理可以实现),
* 但只要 localhost:8080 中写入一次cookie认证,后面的跨域接口都能从 localhost:8080 中获取cookie,从而实现所有的接口都能跨域访问
*/
'Set-Cookie': 'l=a123456;Path=/;Domain=localhost:8080;HttpOnly', // HttpOnly的作用是让js无法读取cookie
'Content-Type': 'application/json;charset=utf-8'
})
response.end(JSON.stringify(data))
break
default:
response.end('404.')
break
}
})
})
server.listen(8080, () => {
console.log('localhost:8080');
})
执行 node server.js
可以看到 cors
跨域成功
Node.js
中间件实现跨域代理,是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite
参数修改响应头中cookie
中域名,实现当前域的cookie写入,方便接口登录认证。
前端代码示例:index.html
doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>中间件代理跨域title>
head>
<body>
<h1>中间件代理跨域h1>
<script>
fetch('http://localhost:8080/api/community/home-api/v1/get-tab-total?username=qq_41887214', {
credentials: 'include', // 前端跨域携带cookie
})
.then(response => response.json())
.then(res => {
console.log(res);
})
script>
body>
html>
执行 npx http-server --port=9000
启动服务
后端 node.js
代码示例:server.js
const http = require('http')
const url = require('url')
const { createProxyMiddleware } = require('http-proxy-middleware')
const server = http.createServer((request, response) => {
if (/^\/api/.test(request.url)) {
const apiProxy = createProxyMiddleware('/api', {
// 代理跨域目标接口
target: 'https://blog.csdn.net',
changeOrigin: true,
pathRewrite: {
'^/api': ''
},
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
proxyRes.headers['Access-Control-Allow-Origin'] = 'http://127.0.0.1:9000'; // 允许访问的域(协议+域名+端口)
proxyRes.headers['Access-Control-Allow-Credentials'] = 'true'; // 后端允许发送Cookie
proxyRes.headers['Content-Type'] = 'application/json;charset=utf-8';
},
// 修改响应信息中的 cookie Domain
cookieDomainRewrite: 'http://127.0.0.1:9000' // 可以为false,表示不修改;如下:
// set-cookie: uuid_tt_dd=10_18756827270-1644377109020-376532; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=.csdn.net;
// set-cookie: uuid_tt_dd=10_18756827270-1644377157391-106781; Expires=Thu, 01 Jan 2025 00:00:00 GMT; Path=/; Domain=http://127.0.0.1:9000;
})
apiProxy(request, response)
} else {
switch (url) {
case '/index.html':
response.end('index.html')
break
case '/search.html':
response.end('search.html')
break
default:
response.end('404.')
}
}
})
server.listen(8080, () => {
console.log('localhost:8080');
})