浏览器跨域问题
- 一、什么是跨域
- 1.背景
- 2.跨域定义
- 3.跨域触发规则
- 4.为什么跨域问题出现频繁
- 5.为什么其他客户端没有跨域这个说法
- 6.为什么服务器之间没有跨域
- 二、怎么解决跨域
- 1.礼貌型解决
- 2.甩锅型解决
- 3.钻空子型解决
一、什么是跨域
首先当我们遇到一个问题,应该尽量去了解问题的成因,不要只是为了解决问题而解决问题。
1.背景
跨域问题的背景是浏览器成为互联网访问最大入口,浏览器安全越来越重要,由此抛出了一个实际场景:
浏览器同时打开了多个网站,比如是a和b。a中加载了a.js文件,如果网站间的资源互通,那b也会获取到a.js,相当于a.js可以获取b的网站数据,也可以进行数据篡改。如果a.js被不法分子利用,是不是很可怕?
2.跨域定义
跨域其实就是浏览器的一种隔离策略。
浏览器提出“Origin”(源)概念,来自不同Origin的对象不能相互干扰,浏览器的同源策略,限制了来自不同源的“document”或脚本,对当前“document”读取或设置某些属性。有点像局部变量和全局变量,网站都规规矩矩用自己的“变量”,没有权利获取其他网站的“变量”。
注意:浏览器对 <link> <script> <img> <frame>这样获取资源的标签没有跨域限制。
3.跨域触发规则
所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
地址 | 是否同源 | 原因 |
---|---|---|
http://a/dir1/a.html | 是 | |
http://a/dir2/b.html | 是 | |
https://a/dir1/a.html | 否 | 协议不一样(http和https) |
http://a:81/dir1/a.html | 否 | 端口不一样 |
http://b/dir1/a.html | 否 | 域名不一样 |
跨域一般是在浏览器控制台输出:
4.为什么跨域问题出现频繁
浏览器同源策略是1995 年由 Netscape 公司提出,之后被其他浏览器厂商采纳。同源策略最开始只是为了保证网站用户的信息安全,防止网站数据被恶意篡改,但随着Web发展,前后端逐渐分离,前端资源和后端资源开始解耦、独立部署,所以跨域问题就需要解决。
一般如果前后端打包部署在同一个服务器,浏览器用同一个源请求前端和后端,就不存在跨域;但前后端部署在同一个服务器,部署的端口不一致,也会发生跨域。
5.为什么其他客户端没有跨域这个说法
首先浏览器作为一种客户端,可能会同时访问多个网站,这些网站“良莠不齐”,你不能保证所有的网站都没有漏洞,不能保证所有网站开发者都是规规矩矩。传统客户端访问都是C/S模式,每个项目有自己独立的客户端,自己的前端访问自己的后端,所以安全性较高。
6.为什么服务器之间没有跨域
首先服务器和服务器之间访问就不像浏览器访问服务器这么频繁,且服务器不会像浏览器这样会存储很多用户的访问凭证(比如Cookie之类),别人家的服务器怎么调用也不告诉你,你就没那么容易获取信息。
二、怎么解决跨域
实例:浏览器正常通过服务器A加载了A.html,但是A.html需要加载服务器B中的某些文件,此时会出现跨域。
1.礼貌型解决
这时候浏览器首先非常礼貌的问服务器B,我能不能访问你的文件呢?如果服务器B说可以,那么浏览器就可以获得文件。
获取文件的过程分成两次请求:
①预检请求:
浏览器首先发起OPTIONS请求(预检请求),此时如果访问目标不同意跨域访问,则会报错403(资源不可用):
如果允许访问则返回200:
②并且会发起真正的资源请求,并且预检请求和实际请求的响应头都有Access-Control-Allow-Origin。
解决方案:
核心思想,在请求的响应头中添加Access-Control-Allow-Origin、Access-Control-Allow-Headers。
比如以SpringBoot为例,可以注入一个响应头的Component
@Component
class CorsFilter implements Filter {
//Cross-origin resource sharing
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
HttpServletRequest reqs = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin",reqs.getHeader("Origin"));
//response.setHeader("Access-Control-Allow-Origin","*");
//response.setHeader("Access-Control-Allow-Credentials", "true");
//response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT, OPTIONS");
//response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept ,X-Custom-Header, Authorization");
chain.doFilter(req, res);
}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void destroy() {}
}
2.甩锅型解决
既然浏览器不让直接访问,那就让服务器A去访问服务器B,反正服务器之间也没有跨域问题,这就是通过代理去解决跨域。
解决方案:
通过代理去解决跨域的具体实现有很多个,比如最常见的nginx解决跨域,vue可以配置代理解决跨域。我们以nginx举例
我们在nginx的server块中配置如下内容:
server {
listen 80;
server_name localhost;
location /api {
proxy_pass http://localhost:8082;
}
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
我们通过http://localhost/进入html;
访问接口时直接访问http://localhost/api/xxx,这样浏览器就认为你们是同源的,也就没有跨域问题了。
3.钻空子型解决
讲同源问题的时候,浏览器对 <link> <script> <img> <frame>这样获取资源的标签没有跨域限制。所以第三种跨域解决方式就是通过这样的标签去获取数据,这种方式就是jsonp方式,目前这种解决跨域的方式用的并不多了。
注意,这些请求资源的标签本质上都是Get请求,因此只能解决Get请求跨域。
比如我们在Body里插入这个标签
<script src="http://localhost:8082/cors"></script>
<script>
function cors(data) {
console.log(data);
}
</script>
然后在后端写这样一个接口
@GetMapping("/cors")
public String corsTest(){
return "cors('hello world');";
}
这时候页面加载时就会触发这个接口,在控制台打印出hello world。
文章部分摘自——吴翰清《白帽子讲Web安全》
更多推荐
浏览器跨域问题
发布评论