跨域及其解决方案
1. 跨域是什么
跨域(cross domain request):是指一个域下的文档或脚本试图去请求另一个域下的资源,二者所在的请求地址不同,域名不同、端口号不同、请求协议不同
举个栗子:
`http://www.123.com/index.html 调用 http://www.123.com/server.PHP (非跨域)`
`http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)`
`http://abc.123.com/index.html 调用 http://def.123.com/server.php (子域名不同:abc/def,跨域)`
`http://www.123.com:8080/index.html 调用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)`
`http://www.123.com/index.html 调用 https://www.123.com/server.php (协议不同:http/https,跨域)`
请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。
跨域是由前端浏览器的同源策略造成的,是浏览器对js施加的安全限制。浏览器不会阻止你向另一个域名发送请求,请求可以发出,但是你拿不到响应的结果。
2. 如果没有同源限制两点危险场景
2.1 没有同源策略限制的接口请求
cookie的存在的目的是让服务端知道谁发出的这次请求,如果你请求了接口进行登录,服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中,服务端就能知道这个用户已经登录过了。
若没有同源策略限制的接口请求,就会出现传说中的CSRF攻击,如下图所示:
2.2 没有同源策略限制的Dom查询
1.有一天你刚睡醒,收到一封邮件,说是你的银行账号有风险,赶紧点进www.yinghang.com改密码。你吓尿了,赶紧点进去,还是熟悉的银行登录界面,你果断输入你的账号密码,登录进去看看钱有没有少了。
2.睡眼朦胧的你没看清楚,平时访问的银行网站是www.yinhang.com,而现在访问的是www.yinghang.com,这个钓鱼网站做了什么呢?
// HTML
<iframe name="yinhang" src="www.yinhang.com"></iframe>
// JS
// 由于没有同源策略的限制,钓鱼网站可以直接拿到别的网站的Dom
const iframe = window.frames['yinhang']
const node = iframe.document.getElementById('你输入账号密码的Input')
console.log(`拿到了这个${node},我还拿不到你刚刚输入的账号密码吗`)
3.同源策略限制
同源策略限制以下几种行为:
1.) Cookie、LocalStorage 和 IndexDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送
这里要注意的是,只有访问类型为xhr(XMLHttpRequest)的才会出现跨域。
4.跨域解决方案有哪些
4.1 前端来处理跨域问题
4.1.1 JSONP
在HTML标签里,一些标签比如script、img这样的获取资源的标签是没有跨域限制的,利用这一点,我们可以这样干:
getJson () {
let url = 'http://10.4.139.133:18080/crossDomain/hello'
this.$jsonp(url, {
params: {},
jsonp: 'jsonpCallback'
})
.then((json) => {
// 返回的jsonp数据不会放这里,而是在 window.jsonpCallback
console.log(json)
})
}
jsonp的缺点:只能发送get一种请求。
2)http-proxy-middleware 代理解决(项目使用vue-cli脚手架搭建)(开发时配置)
devServer: {
open: true,
port: 8080,
proxy: {
'/api': {
target: 'http://10.4.139.133:18080',
changeOrigin: true,
pathRewrite: {
'^/api': '/'
}
},
}
},
4.2 后端处理跨域问题
4.2.1 CORS(跨域资源共享)(cross-origin resourse sharing)
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。通过服务器的response响应头里参数设置 Access-Control-Allow-Origin 就可以开启 CORS。前端无须设置,若要带cookie请求:前后端都需要设置
方法一:过滤器
response.setContentType("application/json; charset=utf-8");
response.setCharacterEncoding("UTF-8");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Methods", "POST, GET,PUT, OPTIONS, DELETE");
response.setHeader("Access-Control-Allow-Origin","*");// 允许访问的域(协议+域名+端口)
response.setHeader("Access-Control-Allow-Credentials", "true"); // 后端允许发送Cookie
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, token,access-control-allow-origin");
方式二:@crossorigin跨域
@ResponseBody
@CrossOrigin
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String hello(HttpServletResponse rsp){
return "jsonpCallback";
}
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)可以写参数
4.2.2 服务器代理
跨域是js的特性,服务端不受影响,所以可以用服务器来请求对应的数据,再返给前端.
nginx反向代理
server {
listen 80;
server_name 10.4.139.32;
location / {
root /usr/share/nginx/html/dist;
index index.html index.htm;
}
location /api/ {
proxy_pass http://10.4.139.133:18080/;
}
}
总结:一般采用cors或nginx代理