【SpringMVC】与权限拦截器冲突导致的Cors跨域设置失效问题
问题描述
前端域名FE.com
向后端域名BE.com
分别请求访问优惠券的列表和提交新增的优惠券,API设计所用的Method分别为Get
和Post
,结果为前一次访问成功而后一次访问失败。这两次请求都是跨域请求,其中请求1包含一个Get
请求,请求2本应该包含一个Options
请求和一个Post
请求,但是只发生了Options
请求。
后端Cors配置
CORS使用SpringMVC自带的<mvc:cors>
标签全局配置,权限检测则实现自定义的拦截器校验Cookie中的Token信息。
<mvc:cors><mvc:mapping allow-credentials="true" allowed-headers="Content-Type" allowed-methods="POST, GET, OPTIONS, DELETE, PUT, HEAD" allowed-origins="http://test.i.meituan.com" max-age="3600" path="/**"/>
</mvc:cors>
<mvc:interceptors><mvc:interceptor><mvc:mapping path="/ajax/shop/**"/> <!--sso回调处理--><bean class="com.dianping.orderdish.framework.interceptor.ShopSsoInterceptor"/></mvc:interceptor>
</mvc:interceptors>
在复杂请求的情况下(即请求2),由于预检请求不会包含Cookie信息(浏览器本身的实现决定其是否发送Cookie,前端无法控制,并且Chrome是不发送的),因此被权限拦截器提前结束,没有输出包含指定头部信息的响应。而一个被浏览器认为合格的预检请求响应必须包含如下的Http头部。
Access-Control-Allow-Origin: http://test.i.meituan.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
Access-Control-Max-Age: 86400
为什么会发生提前结束这种情况?可以对dispatch()
方法进行分析。
Handler和拦截器的执行顺序
DispatchServlet.doDispatch()
方法是SpringMVC的核心入口方法,分析发现所有的拦截器的preHandle()
方法的执行都在实际Handler的方法(比如某个API对应的业务方法)之前,其中任意拦截器返回false
都会跳过后续所有处理过程。而SpringMVC对预检请求的处理则在PreFlightHandler.handleRequest()
中处理,在整个处理链条中出于后置位。由于预检请求中不带Cookie,因此先被权限拦截器拦截。
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
//省略代码
if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
@CrossCors
时序分析
除了在XML中设置全局的CORS配置,Spring提供了@CrossCors
,可以注解在类和方法上,是一种更细粒度的配置方法。SpringMVC把其处理细节包装在getHandler(processedRequest);
中,具体逻辑可以简述为如果请求是预检请求,则返回PreFlightHander
;否则在拦截器末尾添加一个CorsInterceptor
。因此可以看出,@CrossCors
相关的执行和全局的<mvc:cors>
类似,也是滞后于权限拦截器的。
解决方案
方案1:使用Spring-Web自带的CorsFilter
由于CorsFilter
是定义在Web容器中的过滤器(实现了javax.servlet.Filter
),因此其执行顺序先于Servlet,而SpringMVC的入口是DispatchServlet
,因此该Filter会先于SpringMVC的所有拦截器执行。分析代码可知,CorsFilter
会获取单个请求对应的Cors配置做相应的处理。因此可以和(勘误:<mvc:cors>
很好的结合,不需要增加额外的代码。CorsFilter
的构造需要CorsConfigurationSource
实例,并且发生在SpringMVC配置文件解析之前,因此只能放在Spring的配置文件中,否则会发生找不到bean的异常;又由于<mvc:cors>
的解析和Spring核心的解析不共享相同的ParserContext上下文,因此SpringMVC的跨域设置不能植入到CorsFilter
中)
if (CorsUtils.isCorsRequest(request)) {CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);if (corsConfiguration != null) {boolean isValid = this.processor.processRequest(corsConfiguration, request, response);if (!isValid || CorsUtils.isPreFlightRequest(request)) {return;}}
}
filterChain.doFilter(request, response);
方案2:自己实现Interceptor
与方案1类似,把插入Http头的功能实现为SpringMVC的拦截器,然后在<mvc:interceptors>
中声明为第一顺位。
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (request.getHeader(HttpHeaders.ORIGIN) != null) {response.addHeader("Access-Control-Allow-Origin", "http://test.i.meituan.com");response.addHeader("Access-Control-Allow-Credentials", "true");response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT, HEAD");response.addHeader("Access-Control-Allow-Headers", "Content-Type");response.addHeader("Access-Control-Max-Age", "3600");}return true;
}
方案3:增强自定义Interceptor
利用AOP环绕增强所有自定义拦截器的preHandle()
方法,令其跳过预检请求的拦截。
@Around(value = "cut()")
public Object processTx(ProceedingJoinPoint jp) throws Throwable {HttpServletRequest request = (HttpServletRequest) jp.getArgs()[0];if (request != null && CorsUtils.isPreFlightRequest(request)) {return true;} else {return jp.proceed();}
}
Reference
- HTTP访问控制(CORS)
- Token endpoint should permit all HTTP OPTIONS requests to support CORS. #330
- how can i convince spring 4.2 to pass options request through to the controller
【SpringMVC】与权限拦截器冲突导致的Cors跨域设置失效问题相关推荐
- filter导致跨域失效_【SpringMVC】与权限拦截器冲突导致的Cors跨域设置失效问题...
问题描述 前端域名FE.com向后端域名BE.com分别请求访问优惠券的列表和提交新增的优惠券,API设计所用的Method分别为Get和Post,结果为前一次访问成功而后一次访问失败.这两次请求都是 ...
- Spring Boot——Spring Security环境下跨域addCorsMappings与拦截器冲突导致跨域失效解决方案
问题分析 [SpringMVC]与权限拦截器冲突导致的Cors跨域设置失效问题 解决方案 @Beanpublic CorsFilter corsFilter() {//1.添加CORS配置信息Cors ...
- 拦截器---SpringMVC(权限拦截)
拦截器 概述 SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理.开发者可以自己定义一些拦截器来实现特定的功能. **过滤器与拦截器的区别: ...
- 医药采购之操作权限拦截器
用户登录成功根据用户角色从数据库查询用户的操作权限(操作链接),可以进行权限拦截校验,并将操作权限存储至session中,在拦截器中获取用户的操作权限,用户请求url时,如果url在权限操作内放行可以 ...
- (转)SpringMVC学习(十二)——SpringMVC中的拦截器
http://blog.csdn.net/yerenyuan_pku/article/details/72567761 SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter, ...
- 【SpringMVC学习11】SpringMVC中的拦截器
Springmvc的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理.本文主要总结一下springmvc中拦截器是如何定义的,以及测试拦截器的执行情况和使用 ...
- SpringMVC中的拦截器介绍
SpringMvc中的拦截器: SpringMvc拦截器帮我们按照一定规则拦截请求,后根据开发人员自定义的拦截逻辑进行处理: 自定义拦截器需要实现HandlerInterceptor接口: 自定义的拦 ...
- SpringMVC (注解、拦截器、json、Ajax)
SpringMVC 1.回顾MVC 1.1 什么是mvc MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范. 是将业务逻辑.数据.显示分离的方法来 ...
- SpringMVC中的拦截器
SpringMVC中的拦截器 拦截器的作用 Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理. 用户可以自己定义一些拦截器来实现 ...
最新文章
- 在ubuntu10.4安装snort ACID
- boost::uuid模块实现用窄存档测试序列化 uuid的测试程序
- word2026第十套计算机二级,计算机二级第十套练习真题
- SAP CRM my task 6个roundtrip的原理讲解
- u盘 linux centos 5.3,鸟哥linux私房菜学习笔记,U盘安装centos5.3不能正常进入图形界面的问题...
- Java 调用 Caffe_解决 free(): invalid pointer: 0x00000000019ff700 运行时报错(caffe)(libtool使用)...
- 计算机软考有学历限制吗,软考中级职称申请积分还需要学历吗?
- Android中的观察者DataSetObservable和DataSetObserver
- 面对海量请求,缓存设计还应该考虑哪些问题?(转)
- Error running ...: No jdk for module
- 【jzoj2173】【DFS】无根树
- 浅谈AI芯片的简要发展历史
- java 生成的excel 用editplus 打开是乱码_「excel打开是乱码」excel出现中文乱码的解决教程 - seo实验室...
- 用Python给弟弟生成1000道算术题
- 《TPM原理及应用指南》深入研读 —— TPM介绍
- java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11GraphicsEnvironment
- payssion支付
- 【js逆向】md5加密参数破解
- 函数 fseek() 使用说明
- UVA 10118 dfs