怎么解决跨域问题?
什么是跨域?
一个网页向另一个不同域名/不同协议/不同端口的网页请求资源,这就是跨域。
跨域产生的原因:出于安全考虑,浏览器实施了同源策略。在当前域名请求网站中,默认不允许通过 ajax 请求发送到其他域名。违背同源策略就是跨域
同源策略
什么是同源策略
同源策略是由 Netscape 提出的一个著名的安全策略,现在所有支持 JavaScript 的浏览器都会使用这个策略。
同源策略要求网页只能从同一个域名(协议、域名、端口号都相同)加载资源,而不能直接访问其他域名下的资源。
为什么要使用同源策略
同源策略的目的是防止恶意网站通过脚本等方式获取用户的敏感信息或进行其他恶意操作。
如果没有同源策略限制,恶意网站就可以通过跨域请求攻击其他网站,窃取用户的信息。
如果网页之间不满足同源要求,将不能:
- 共享Cookie、LocalStorage、IndexDB
- 获取DOM
- AJAX请求不能发送
跨域的解决方案
CORS(跨域资源共享)
CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS 是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持 get 和 post 请求。
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
怎么使用 CORS: CORS 通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
// 代码示例
app.all('/cors-server', (request, response) => {
//设置响应头
//响应首部中可以携带一个 Access-Control-Allow-Origin 字段,表示允许哪些源站访问
response.setHeader("Access-Control-Allow-Origin", "*");
// response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:5500");
//Access-Control-Allow-Headers 首部字段用于预检请求的响应。其指明了实际请求中允许携带的首部字
response.setHeader("Access-Control-Allow-Headers", '*');
//Access-Control-Allow-Methods 首部字段用于预检请求的响应。其指明了实际请求所允许使用的 HTTP
response.setHeader("Access-Control-Allow-Method", '*');
response.send('hello CORS');
});
以下是具体的一些字段:
- Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin> | *
origin 参数的值指定了允许访问该资源的外域 URI。对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符,表示允许来自所有域的请求。
- Access-Control-Allow-Headers
Access-Control-Allow-Headers 指明了实际请求中允许携带的首部字段。
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
- Access-Control-Allow-Methods
Access-Control-Allow-Methods 指明了实际请求所允许使用的 HTTP 方法。
Access-Control-Allow-Methods: <method>[, <method>]*
JSONP
JSONP 是什么?
JSONP(JSON with Padding),是一个非官方的跨域解决方案,纯粹凭借程序员的聪明才智开发出来,只支持 get 请求。
JSONP 是怎么工作的?
在网页有一些标签天生具有跨域能力,比如:img link iframe script。
JSONP 就是利用 script 标签的跨域能力来发送请求的。
// 1. 动态的创建一个 script 标签------------------------------------------------------------
var script = document.createElement("script");
//2. 设置 script 的 src, 设置回调函数
script.src = "http://localhost:3000/testAJAX?callback=abc";
function abc(data) {
alert(data.name);
};
// 3. 将 script 添加到 body 中
document.body.appendChild(script);
// 4. 服务器中路由的处理------------------------------------------------------
router.get("/testAJAX", function (req, res) {
console.log("收到请求");
var callback = req.query.callback;
var obj = {
ame: "孙悟空",
age: 18
}
res.send(callback + "(" + JSON.stringify(obj) + ")");
});
Axios 中的实现:
this.$http = axios;
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
JSONP的缺点:
- 具有局限性, 仅支持get方法
- 不安全,可能会遭受XSS攻击
nginx 反向代理
同源策略仅是针对浏览器的安全策略。服务器端调用 HTTP 接口只是使用 HTTP 协议,不需要同源策略,也就不存在跨域问题。
**实现思路:**通过 Nginx 配置一个代理服务器域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域访问。
nginx 具体配置:
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}