美文网首页
跨域请求403详解

跨域请求403详解

作者: 愤怒的红蓝色 | 来源:发表于2019-07-15 17:17 被阅读0次

    0、环境说明

    1、下文中跨域实现为服务器域名 http://yaogy.jd.com 向本地项目 leo.com 发起跨域请求,本地进行debug。

    2、本地项目 Spring 版本为 4.3.0。

    跨域的实现方式有很多种,注解、过滤器、拦截器都能很好的实现跨域的功能,但在实际应用中却发现在同一个跨域实现、同一个 controller 类下,有的跨域请求成功,有的跨域请求返回 403,如图1 所示。

    image

    1、基于拦截器的跨域403响应

    图1所示请求中,采取的是通过过滤器的方式实现跨域,ajax 请求方式为 GET请求,content-type 为 application/json,是一个复杂请求。初始过滤器代码如下:

    
    public class CorsFilter extends OncePerRequestFilter {
    
        @Override
    
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
            String originUrl = request.getHeader("origin");
    
            if(!StringUtils.isEmpty(originUrl)){
    
                //自定义跨域域名检查
    
                boolean isAllow = checkAllow(originUrl);
    
                if (isAllow) {
    
                    response.setHeader("Access-Control-Allow-Origin", originUrl);
    
                }
    
                response.setHeader("Access-Control-Allow-Credentials", "true");
    
                response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    
                response.setHeader("Access-Control-Max-Age", "1800");//30分钟
    
                response.setHeader("Access-Control-Allow-Headers", "x-requested-with, content-type");
    
            }
    
            filterChain.doFilter(request, response);
    
        }
    
    }
    
    

    上述拦截器在拦截普通跨域请求时能够正常跨域,但遇到复杂跨域请求时,在发起预请求的时候预请求阶段就返回 403 ,跨域失败,结果如图1所示。

    image

    如图2所示,该方法的全限定名为 org.springframework.web.cors.DefaultCorsProcessor#processRequest,对于CorsConfiguration对象为空的预请求,将直接返回403,至于CorsConfiguration,可以参考另一篇文章

    Tnlj:CORS与@CrossOrigin详解​zhuanlan.zhihu.com

    大致流程为:Spring 容器在启动的时候会扫描每一个添加了 @Controller 注解的类、@RequestMapping注解的方法,之后判断类或者方法上是否有 @CrossOrigin 注解,并将 @CrossOrigin 注解中的内容转换成 CorsConfiguration 对象,具体转换逻辑如图3所示:

    image

    而对于基于过滤器实现的跨域,没有 @CrossOrigin 注解的加持,CorsConfiguration 对象自然为空,而在Spring对跨域请求的处理逻辑中,对于CorsConfiguration 对象为空的预请求是会执行 rejectRequest 方法,也就是返回状态码 403。既然 Spring 对跨域请求的处理逻辑我们无法改变,所以我们可以在过滤器中添加对 预请求的单独处理或者采用注解的方式解决复杂请求的跨域403响应。修改后的过滤器如下:

    
    public class CorsFilter extends OncePerRequestFilter {
    
        @Override
    
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
            String originUrl = request.getHeader("origin");//请求的地址
    
            if(!StringUtils.isEmpty(originUrl)){
    
                //自定义跨域域名检查
    
                boolean isAllow = checkAllow(originUrl);
    
                if (isAllow) {
    
                    response.setHeader("Access-Control-Allow-Origin", originUrl);
    
                }
    
                response.setHeader("Access-Control-Allow-Credentials", "true");
    
                response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
    
                response.setHeader("Access-Control-Max-Age", "1800");//30分钟
    
                response.setHeader("Access-Control-Allow-Headers", "x-requested-with, content-type");
    
                //对预请求单独处理
    
                String method = request.getMethod();
    
                if (method.equalsIgnoreCase("OPTIONS")){
    
                    response.setStatus(HttpServletResponse.SC_OK);
    
                    return;
    
                }
    
            }
    
            filterChain.doFilter(request, response);
    
        }
    
    }
    
    

    2、基于注解的跨域403响应

    基于注解的跨域实现能够解决 CorsConfiguration 对象为空的问题,进而解决了在拦截器的实现方式中预请求403的问题。但注解并不能解决所有问题,注解使用不当的时候仍然可能返回403响应。

    问题产生场景:

    1、方法上的注解未设置 methods 属性

    2、ajax请求方法为 POST(或其他非HEAD、GET)方法。

    问题产生原因:

    image

    如图4所示,当方法上的 @CrossOrigin 注解未进行任何配置时,获得的 allowMethods 对象为空导致返回 403 响应。checkMethods方法代码如下:

    
    protected List<HttpMethod> checkMethods(CorsConfiguration config, HttpMethod requestMethod) {
    
            //方法的具体逻辑见图5
    
            return config.checkHttpMethod(requestMethod);
    
        }
    
    
    image

    由图5可知,当@CrossOrigin 注解未配置 methods 属性时,默认只允许 GET、HEAD 方法的访问,对于其他的请求方法都将返回403响应。

    解决办法:

    给方法添加跨域注解时增加需要支持的方法,比如:@CrossOrigin(methods = {RequestMethod.GET, RequestMethod.POST})

    3、总结

    a、使用过滤器、拦截器等的配置方式无法解决复杂请求的预请求的问题,但对于POST方法的简单请求不会出现问题。

    b、使用注解的方式在不设置跨域方法的情况下对非 GET、HEAD 方法的请求会出现403的响应,但对于复杂请求无需做额外的逻辑处理。

    知乎链接:跨域请求403 - 知乎

    相关文章

      网友评论

          本文标题:跨域请求403详解

          本文链接:https://www.haomeiwen.com/subject/wgqokctx.html