关于跨域问题的思考

本文详细介绍了跨域问题的产生原因、浏览器的同源策略及其限制,并提供了三种解决跨域的方法:JSONP、代理服务器和CORS,包括各自的实现原理和应用场景。同时,文章探讨了在SpringBoot中处理CORS时可能遇到的问题及解决方案。
摘要由CSDN通过智能技术生成

  • 这是一篇本应很久之前就该发布的博客,奈何我健忘(懒)

00 | 可恶!报错了!

  • 某天,我在写代码,但是报错了,如下图:
    在这里插入图片描述
    于是我进行debug,在出现异常的地方,我发现返回的 response 是 undefined 的,并且 message 消息中只有一个"Network Error"。 于是一番百度,发现是跨域问题。
  • 有个问题,浏览器是发不出请求,还是能发送请求,但是回来的请求被拦截了呢?
    可以用charles抓个包,发现charles不仅抓到了一个请求,甚至连返回内容都有了,所以对于普通的网络请求,浏览器会先发请求,待收到请求的返回之后,校验请求的response headers,再根据同源策略和response headers的内容判断能不能允许请求正常返回给js代码。

01 | 什么是跨域问题

  • 前端调用的后端接口不属于同一个域(域名或端口不同),就会产生跨域问题,也就是说你的应用访问了该应用域名或端口之外的域名或端口。
  • 为什么会出现response: undefined ?
    请求已经发出去了,服务端也接收到并处理了,但是返回的响应结果不是浏览器想要的结果,所以浏览器将响应结果给拦截了,所以会看到response是undefined。

02 | 为什么会发生跨域问题

那么问题来了,为什么浏览器端会将返回的结果给拦截掉?

  • 因为有浏览器的同源策略哦宝,服务器之间没有跨域的说法,所以跨域只存在于浏览器上

浏览器的同源策略是什么

  • 所谓“同源”就是指"协议+域名+端口"三者相同,如果没有同源策略的保护,就会产生CSRF攻击(跨站请求伪造),CSRF攻击就是说,比如我去 alinxi.top 上买了件衣服,并且输入了支付账号密码进行支付,浏览器在本地缓存了我银行卡的账号密码的cookid,但是我不小心,点了个钓鱼网站,他获取我的cookie,伪装成是我进行转账。

同源策略会限制什么行为呢

  1. Cookie 、LocalStorage 和 IndexDB无法读取
  2. 无法获取或操作另一个资源的DOM
  3. AJAX请求不能发送

03 | 跨域问题解决思路

哎呀,什么是CORS呢?

  • CORS是"跨域资源共享"(Cross Origin Resource Sharing),它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了只能发送同源请求的限制。

怎么解决跨域问题呢?

  • 法一:甩锅给前端,让他用JSONP啦
    原理就是利用浏览器对于script、img标签在跨域上的“特殊处理”,它们并没有跨域限制。因此,当我们需要用到跨域请求时,可以在浏览器中构建一个标签,设置他的src属性为要请求的地址,让这个标签去请求服务器。but,JSONP只能处理GET请求,如果想处理POST请求的话,可以用iframe+form来解决,用iframe发送数据,使用form执行表单提交。前端用jsonp,相应的后端也要进行变动,springboot1.5版本里,添加一个切面来支持JSONP请求,springboot2.多版本中废弃了一个类。

  • 法二:用个代理服务器吧
    比如说Nginx和webpack-dev-server,webpack-dev-serve的原理主要也是服务器之间的请求是没有跨域的,它会用express起一个服务,然后将所有请求传到指定的URI。这个时候我们可以使用Nginx来做转发,进行server{}配置一下就好了。
    代理服务器的曲线救国方式就是说,先让ajax请求同源地址,然后让同源的服务器再请求目标地址,得到目标地址响应后,再将响应转交给客户端。因为代理服务端用的是服务器技术比如php什么的,去请求目标服务器,不是用js,所以不会有同源策略的限制。因为发了两次请求,所以效率上会差一点。

  • 法三:那就后端辛苦一下吧:CORS
    通过增加一系列请求头和响应头来实现跨域,比如Access-Control-Request-HeadersAccess-Control-Allow-Origin是该字段表示,服务端接收哪些来源的域的请求。后端解决方式种类如下:

    1. 在 Java Web 中,可以添加一个拦截器来设置相应的参数,而如果用springboot的话直接在 Controller 类上加上 @CrossOrigin注解就可以了。

    2. 或者通过 CorsRegistry 设置全局跨域配置,如果你使用的是 Spring Boot,推荐的做法是只定义一个 WebMvcConfigurer 的Bean。

      对于第二种方式在没有定义 拦截器(Interceptor) 的时候,使用一切正常,但是如果你有一个全局的拦截器用来检测用户的登录态,当自定义拦截器返回true时,一切正常,但是当拦截器抛出异常(或者返回false)时,后续的CORS设置将不会生效。主要是因为,AbstractHandlerMapping在添加CorsInterceptor的时候,是将 Cors 的拦截器 加在拦截器链的最后,那就会造成上面说的问题,在自定义拦截器中抛出异常之后,CorsInterceptor 拦截器就没有机会执行向 response 中设置 CORS 相关响应头了。相应的解决方案,就是将用来处理 Cors 的拦截器 CorsInterceptor 加在拦截器链的第一个位置就ok了。这是一个国外的哥提的issue,感觉是一个可行的解决方案,但是 Spring Boot 的成员认为这不是 Spring Boot 的Bug,而是 Spring Framework 的 Bug,所以将这个issue关闭了。

    3. 通过CorsFilter 过滤器设置全局跨域配置,过滤器可以而拦截器不行的主要原因是:因为过滤器依赖于 Servlet 容器,基于函数回调,它可以对几乎所有请求进行过滤。而拦截器是依赖于 Web 框架(如Spring MVC框架),基于反射通过AOP的方式实现的。因为过滤器在触发上是先于拦截器的,但是如果有多个过滤器的话,也需要将 CorsFilter 设置为第一个过滤器才行


04 | 参考资料

完成。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值