简述跨域以及解决方案

基本概念

基于浏览器的基本安全功能(同源策略),只允许在同一域内的资源交互,一般而言同域指的是:相同协议(protocol)、相同主机(host)、相同端口(port)。如果请求跨域则可能出现一下几种情况。

  1. 无法读取Cookie、LocalStorage 和 IndexDB
  2. DOM 和 JS 对象无法获取
  3. Ajax请求失败

问题

下面结合笔者实践中遇到的一个问题来简要说明如何解决请求跨域。

场景

ajax提交psot请求,默认contentType为表单提交格式(application/x-www-form-urlencoded)请求可正常相应,当设置contentType为“application/json时”,请求头出现Provisional headers are shown

寻找问题

Provisional headers are shown出现有以下几种情况

  1. 请求被浏览器插件拦截
  2. 服务器出错或者响应超时
  3. 请求跨域被浏览器拦截

接下来慢慢分析的问题属于哪一种,本地chrome有插件,但在关闭插件后仍提示,第一种情况排除。请求对应的服务端逻辑几近于无只有接收参数并打印,且在请求过程中未报错,所以排除第二种情况。上面提到的“Ajax请求提示Provisional headers are shown”,正好和第三种情况对应。

到这里就知道问题是请求跨域导致的,但为什么在contentType默认时未发生跨域,请带着疑问往下看。

在请求跨域时会向服务端先发送PreFlight(option请求)验证是否能接收当前请求类型、是否被允许的、是否需要Credentials(认证信息)。但有特殊情况需要注意,在请求是simple request(简单请求)时,则在跨域前不会发送PreFlight。

简单请求

  1. 请求类型必须是get、post、head其中之一
  2. HTTP Headers不能自定义
  3. contentType必须是application/x-www-form-urlencoded、multipart/form-data、text/plain其中之一

这就很好的说明了问题的原因。post请求设置contentType为application/json使请求成为非简单请求,导致PreFlight验证未通过。

解决方案

非简单请求PreFlight验证未通过,会导致请求失败。那反之让PreFlight验证通过就能很好的解决跨域的问题。刚刚提到PreFlight验证请求类型、域、认证信息,在http协议中由以下几个属性控制:

  1. Access-Control-Allow-Origin:表示请求允许的域,设置为“*”即表示允许所有host发来的请求
  2. Access-Control-Allow-Credentials:表示请求是否携带cookie,默认不携带。值只能为true,如果不要求携带cookie可删除此属性配置
  3. Access-Control-Allow-Methods:表示支持的请求类型,字符串请求类型之间逗号隔开。如:”POST,GET,PUT“
  4. Access-Control-Max-Age:表示本次验证的有效期,整型单位为秒
  5. Access-Control-Allow-Headers:表明服务器支持的所有请求头字段

鉴于笔者是后端开发,本文只讨论后端的处理方式

  1. @CrossOrigin注解,Spring4.2及其以上版本可使用该注解,可以是类级也可以是方法级,注解打在controller类上则说明该controller下的所有方法都支持跨域,如果打在方法上则说明该方法支持跨域,示例代码如下

    @Controller
    @RequestMapping(value = "test")
    @CrossOrigin
    public class TestController {
    
        @CrossOrigin
        @RequestMapping(value = "/test")
        public String test() {
            return "success";
        }
    
    }
    
  2. 注解最大范围只能解决当个controller的跨域问题,如果想解决整个项目的跨域问题,我们还有更优雅的处理方式,那就是web三大组件之一的filter,通过自定义filter来返回上述请求头从而解决跨域,示例代码如下

    @WebFilter(filterName = "CorsFilter")
    @Configuration
    public class CorsFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletResponse response = (HttpServletResponse) res;
            response.setHeader("Access-Control-Allow-Origin","*");
            response.setHeader("Access-Control-Allow-Credentials", "true");
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
            chain.doFilter(req, res);
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值