为什么会出现跨域问题
浏览器的同源策略是跨域问题的根本所在。同源策略是一个非常重要的安全策略,它用于限制两个源之间在浏览器上进行资源交互。如果缺少了同源策略,网站或服务器很容易受到 XSS、CSFR 等攻击。
那么同源是什么意思?同源表示两个 URL 的 origin
是相同的,origin
由 protocol
、host
和 port
组成。
{
"protocol": "http:",
"host": "web.example.com",
"port": "",
"origin": "http://web.example.com"
}
以下方法假设:
前端域名:http://web.domain.com
后端域名:http://server.domain.com
运行在 http://web.domain.com 的 JavaScript 代码发起了一个到 http://server.domain.com/api/list 的 AJAX 请求。
方法一:设置 CORS 允许跨域资源共享(最直接)
设置 CORS 是最直接的方法。该方法无需前端操作,只需后端在返回响应时设置响应头即可:
指定允许访问的域名:
{
"Access-Control-Allow-Origin": "http://web.domain.com"
}
或者使用通配符 *
,表示所有网站都可以访问资源:
{
"Access-Control-Allow-Origin": "*"
}
方法二:使用 Nginx 反向代理(最兼容)
由于同源策略仅存在于浏览器之间,服务器之间的交互不需要遵循同源策略。
所以我们这里可以使用 Nginx 作为 Web 服务器,监听并接收来自外部(其他服务器)请求,将接收到的请求使用和本机相同的域名转发到后端,然后再将响应转发给前端。
使用 Nginx 反向代理的必要设置如下:
// nginx.conf
server {
listen 80;
server_name web.domain.com;
location / {
proxy_pass server.domain.com;
}
}
修改后需要重启 Nginx 服务器:
sudo nginx -s reload
方法三:前端使用 Webpack 代理(最方便)
同样也是利用服务器向服务器请求无需遵循同源策略的特点。只不过这里我们使用 Webpack 作为本地的代理服务器。
该方法无需后端操作,只需在 webpack.config.js
中配置即可:
// webpack.config.js
module.exports = {
devServer: {
proxy: {
// 接口前缀
'/api': {
target: 'http://server.domain.com', // 指定服务器地址
},
},
},
}
方法四:使用 JSONP(最古老)
JSONP(JSON with Padding)主要是利用 标签加载脚本不受同源策略的限制这一特性,来加载需要请求的资源。
该方法需要后端和前端同时设置,且仅支持 GET 请求。
后端代码示例(以 node.js 为例):
const querystring = require('querystring')
const http = require('http')
const server = http.createServer()
server.on('request', function (req, res) {
const params = qs.parse(req.url.split('?')[1])
const fn = params.callback
res.writeHead(200, { 'Content-Type': 'text/javascript' }) // 设置返回的 MIME 类型为 text/javascript
res.write(fn + '(' + JSON.stringify(params) + ')') // 回调函数
res.end()
})
server.listen('8080')
前端代码示例:
let script = document.createElement('script')
// 在 URL 后指定一个回调函数
script.src = 'http://server.domain.com/api/list?callback=handleCallback'
document.head.appendChild(script)
// 回调执行函数
function handleCallback(res) {
console.log(res)
}
方法五:使用 WebSocket(最新)
HTML5 定义了 Websocket 协议,该协议主要用于服务器和浏览器之间的持久化连接,并且没有同源策略的限制。
该方法需要后端提供 Websocket 接口,前端进行接收。
前端代码示例:
let ws = new WebSocket('ws://server.domain.com/api/list')
ws.onopen = function () {
console.log('连接成功')
}
ws.onmessage = function (res) {
console.log(res.data)
}
ws.onclose = function () {
console.log('连接关闭')
}
后端代码示例(以 node.js 为例):
const WebSocket = require('ws')
const server = new WebSocket.Server({ port: 8080 })
server.on('connection', function (socket) {
socket.on('message', function (data) {
socket.send(data)
})
})
但如果仅仅是为了实现跨域,不推荐使用 Websocket。