跨域ajax post json 403,跨域请求403详解

0、环境说明

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

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

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

15eef2914e36

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所示。

15eef2914e36

image

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

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

15eef2914e36

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)方法。

问题产生原因:

15eef2914e36

image

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

protected List checkMethods(CorsConfiguration config, HttpMethod requestMethod) {

//方法的具体逻辑见图5

return config.checkHttpMethod(requestMethod);

}

15eef2914e36

image

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

解决办法:

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

3、总结

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值