javascript
filter导致跨域失效_【SpringMVC】与权限拦截器冲突导致的Cors跨域设置失效问题...
問題描述
前端域名FE.com向后端域名BE.com分別請求訪問優惠券的列表和提交新增的優惠券,API設計所用的Method分別為Get和Post,結果為前一次訪問成功而后一次訪問失敗。這兩次請求都是跨域請求,其中請求1包含一個Get請求,請求2本應該包含一個Options請求和一個Post請求,但是只發生了Options請求。
后端Cors配置
CORS使用SpringMVC自帶的標簽全局配置,權限檢測則實現自定義的攔截器校驗Cookie中的Token信息。
allowed-methods="POST, GET, OPTIONS, DELETE, PUT, HEAD"
allowed-origins="http://test.i.meituan.com"
max-age="3600" path="/**"/>
在復雜請求的情況下(即請求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相關的執行和全局的類似,也是滯后于權限攔截器的。
解決方案
方案1:使用Spring-Web自帶的CorsFilter
由于CorsFilter是定義在Web容器中的過濾器(實現了javax.servlet.Filter),因此其執行順序先于Servlet,而SpringMVC的入口是DispatchServlet,因此該Filter會先于SpringMVC的所有攔截器執行。分析代碼可知,CorsFilter會獲取單個請求對應的Cors配置做相應的處理。因此可以和很好的結合,不需要增加額外的代碼。(勘誤:CorsFilter的構造需要CorsConfigurationSource實例,并且發生在SpringMVC配置文件解析之前,因此只能放在Spring的配置文件中,否則會發生找不到bean的異常;又由于的解析和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的攔截器,然后在中聲明為第一順位。
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
總結
以上是生活随笔為你收集整理的filter导致跨域失效_【SpringMVC】与权限拦截器冲突导致的Cors跨域设置失效问题...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux系统下替换图片,Linux(u
- 下一篇: 计算机应用基础案例实训教程,计算机应用基