最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring MVC cors跨域?qū)崿F(xiàn)源碼解析

 更新時間:2017年02月09日 09:20:08   作者:出門向左  
本文主要介紹了spring MVC cors跨域?qū)崿F(xiàn)源碼解析。具有很好的參考價值,下面跟著小編一起來看下吧

名詞解釋:跨域資源共享(Cross-Origin Resource Sharing)

簡單說就是只要協(xié)議、IP、http方法任意一個不同就是跨域。

spring MVC自4.2開始添加了跨域的支持。

跨域具體的定義請移步mozilla查看

使用案例

spring mvc中跨域使用有3種方式:

在web.xml中配置CorsFilter

<filter>
 <filter-name>cors</filter-name>
 <filter-class>org.springframework.web.filter.CorsFilter</filter-class>
</filter>
<filter-mapping>
 <filter-name>cors</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

在xml中配置

// 簡單配置,未配置的均使用默認(rèn)值,就是全面放開
<mvc:cors> 
 <mvc:mapping path="/**" /> 
</mvc:cors> 
// 這是一個全量配置
<mvc:cors> 
 <mvc:mapping path="/api/**" 
  allowed-origins="http://domain1.com, http://domain2.com" 
  allowed-methods="GET, PUT" 
  allowed-headers="header1, header2, header3" 
  exposed-headers="header1, header2" allow-credentials="false" 
  max-age="123" /> 
  <mvc:mapping path="/resources/**" 
  allowed-origins="http://domain1.com" /> 
</mvc:cors> 

使用注解

@CrossOrigin(maxAge = 3600) 
@RestController 
@RequestMapping("/account") 
public class AccountController { 
 @CrossOrigin("http://domain2.com") 
 @RequestMapping("/{id}") 
 public Account retrieve(@PathVariable Long id) { 
  // ... 
 } 
} 

涉及概念

  • CorsConfiguration 具體封裝跨域配置信息的pojo
  • CorsConfigurationSource request與跨域配置信息映射的容器
  • CorsProcessor 具體進(jìn)行跨域操作的類
  • 諾干跨域配置信息初始化類
  • 諾干跨域使用的Adapter

涉及的java類:

封裝信息的pojo

CorsConfiguration

存儲request與跨域配置信息的容器

CorsConfigurationSource、UrlBasedCorsConfigurationSource

具體處理類

CorsProcessor、DefaultCorsProcessor

CorsUtils

實現(xiàn)OncePerRequestFilter接口的Adapter

CorsFilter

校驗request是否cors,并封裝對應(yīng)的Adapter

AbstractHandlerMapping、包括內(nèi)部類PreFlightHandler、CorsInterceptor

讀取CrossOrigin注解信息

AbstractHandlerMethodMapping、RequestMappingHandlerMapping

從xml文件中讀取跨域配置信息

CorsBeanDefinitionParser

跨域注冊輔助類

MvcNamespaceUtils

debug分析

要看懂代碼我們需要先了解下封裝跨域信息的pojo--CorsConfiguration

這邊是一個非常簡單的pojo,除了跨域?qū)?yīng)的幾個屬性,就只有combine、checkOrigin、checkHttpMethod、checkHeaders。

屬性都是多值組合使用的。

 // CorsConfiguration
 public static final String ALL = "*";
 // 允許的請求源
 private List<String> allowedOrigins;
 // 允許的http方法
 private List<String> allowedMethods;
 // 允許的請求頭
 private List<String> allowedHeaders;
 // 返回的響應(yīng)頭
 private List<String> exposedHeaders;
 // 是否允許攜帶cookies
 private Boolean allowCredentials;
 // 預(yù)請求的存活有效期
 private Long maxAge;

combine是將跨域信息進(jìn)行合并

3個check方法分別是核對request中的信息是否包含在允許范圍內(nèi)

配置初始化

在系統(tǒng)啟動時通過CorsBeanDefinitionParser解析配置文件;

加載RequestMappingHandlerMapping時,通過InitializingBean的afterProperties的鉤子調(diào)用initCorsConfiguration初始化注解信息;

配置文件初始化

在CorsBeanDefinitionParser類的parse方法中打一個斷點。

CorsBeanDefinitionParser的調(diào)用棧

通過代碼可以看到這邊解析

跨域信息的配置可以以path為單位定義多個映射關(guān)系。

解析時如果沒有定義則使用默認(rèn)設(shè)置

// CorsBeanDefinitionParser
if (mappings.isEmpty()) {
 // 最簡配置時的默認(rèn)設(shè)置
 CorsConfiguration config = new CorsConfiguration();
 config.setAllowedOrigins(DEFAULT_ALLOWED_ORIGINS);
 config.setAllowedMethods(DEFAULT_ALLOWED_METHODS);
 config.setAllowedHeaders(DEFAULT_ALLOWED_HEADERS);
 config.setAllowCredentials(DEFAULT_ALLOW_CREDENTIALS);
 config.setMaxAge(DEFAULT_MAX_AGE);
 corsConfigurations.put("/**", config);
}else {
 // 單個mapping的處理
 for (Element mapping : mappings) {
  CorsConfiguration config = new CorsConfiguration();
  if (mapping.hasAttribute("allowed-origins")) {
   String[] allowedOrigins = StringUtils.tokenizeToStringArray(mapping.getAttribute("allowed-origins"), ",");
   config.setAllowedOrigins(Arrays.asList(allowedOrigins));
  }
  // ...
 }

解析完成后,通過MvcNamespaceUtils.registerCorsConfiguratoions注冊

這邊走的是spring bean容器管理的統(tǒng)一流程,現(xiàn)在轉(zhuǎn)化為BeanDefinition然后再實例化。

// MvcNamespaceUtils
 public static RuntimeBeanReference registerCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations, ParserContext parserContext, Object source) {
  if (!parserContext.getRegistry().containsBeanDefinition(CORS_CONFIGURATION_BEAN_NAME)) {
   RootBeanDefinition corsConfigurationsDef = new RootBeanDefinition(LinkedHashMap.class);
   corsConfigurationsDef.setSource(source);
   corsConfigurationsDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
   if (corsConfigurations != null) {
    corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations);
   }
   parserContext.getReaderContext().getRegistry().registerBeanDefinition(CORS_CONFIGURATION_BEAN_NAME, corsConfigurationsDef);
   parserContext.registerComponent(new BeanComponentDefinition(corsConfigurationsDef, CORS_CONFIGURATION_BEAN_NAME));
  }
  else if (corsConfigurations != null) {
   BeanDefinition corsConfigurationsDef = parserContext.getRegistry().getBeanDefinition(CORS_CONFIGURATION_BEAN_NAME);   corsConfigurationsDef.getConstructorArgumentValues().addIndexedArgumentValue(0, corsConfigurations);
  }
  return new RuntimeBeanReference(CORS_CONFIGURATION_BEAN_NAME);
 }

注解初始化

在RequestMappingHandlerMapping的initCorsConfiguration中掃描使用CrossOrigin注解的方法,并提取信息。

RequestMappingHandlerMapping_initCorsConfiguration

// RequestMappingHandlerMapping
 @Override
 protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
  HandlerMethod handlerMethod = createHandlerMethod(handler, method);
  CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(), CrossOrigin.class);
  CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
  if (typeAnnotation == null && methodAnnotation == null) {
   return null;
  }
  CorsConfiguration config = new CorsConfiguration();
  updateCorsConfig(config, typeAnnotation);
  updateCorsConfig(config, methodAnnotation);
  // ... 設(shè)置默認(rèn)值
  return config;
 } 

跨域請求處理

HandlerMapping在正常處理完查找處理器后,在AbstractHandlerMapping.getHandler中校驗是否是跨域請求,如果是分兩種進(jìn)行處理:

  • 如果是預(yù)請求,將處理器替換為內(nèi)部類PreFlightHandler
  • 如果是正常請求,添加CorsInterceptor攔截器

拿到處理器后,通過請求頭是否包含Origin判斷是否跨域,如果是跨域,通過UrlBasedCorsConfigurationSource獲取跨域配置信息,并委托g(shù)etCorsHandlerExecutionChain處理

UrlBasedCorsConfigurationSource是CorsConfigurationSource的實現(xiàn),從類名就可以猜出這邊request與CorsConfiguration的映射是基于url的。getCorsConfiguration中提取request中的url后,逐一驗證配置是否匹配url。

 // UrlBasedCorsConfigurationSource
 public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
  String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
  for(Map.Entry<String, CorsConfiguration> entry : this.corsConfigurations.entrySet()) {
   if (this.pathMatcher.match(entry.getKey(), lookupPath)) {
    return entry.getValue();
   }
  }
  return null;
 }
 // AbstractHandlerMapping
 public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  Object handler = getHandlerInternal(request);
  // ...
  HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
  if (CorsUtils.isCorsRequest(request)) {
   CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
   CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
   CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
   executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
  }
  return executionChain;
 }
 // HttpHeaders
 public static final String ORIGIN = "Origin";
 // CorsUtils
 public static boolean isCorsRequest(HttpServletRequest request) {
  return (request.getHeader(HttpHeaders.ORIGIN) != null);
 }

通過請求頭的http方法是否options判斷是否預(yù)請求,如果是使用PreFlightRequest替換處理器;如果是普通請求,添加一個攔截器CorsInterceptor。

PreFlightRequest是CorsProcessor對于HttpRequestHandler的一個適配器。這樣HandlerAdapter直接使用HttpRequestHandlerAdapter處理。

CorsInterceptor 是CorsProcessor對于HnalderInterceptorAdapter的適配器。

 // AbstractHandlerMapping
 protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
   HandlerExecutionChain chain, CorsConfiguration config) {
  if (CorsUtils.isPreFlightRequest(request)) {
   HandlerInterceptor[] interceptors = chain.getInterceptors();
   chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
  }
  else {
   chain.addInterceptor(new CorsInterceptor(config));
  }
  return chain;
 }
 private class PreFlightHandler implements HttpRequestHandler {
  private final CorsConfiguration config;
  public PreFlightHandler(CorsConfiguration config) {
   this.config = config;
  }
  @Override
  public void handleRequest(HttpServletRequest request, HttpServletResponse response)
    throws IOException {

   corsProcessor.processRequest(this.config, request, response);
  }
 }
 private class CorsInterceptor extends HandlerInterceptorAdapter {
  private final CorsConfiguration config;
  public CorsInterceptor(CorsConfiguration config) {
   this.config = config;
  }
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
    Object handler) throws Exception {

   return corsProcessor.processRequest(this.config, request, response);
  }
 }
 // CorsUtils
 public static boolean isPreFlightRequest(HttpServletRequest request) {
  return (isCorsRequest(request) && request.getMethod().equals(HttpMethod.OPTIONS.name()) &&
    request.getHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD) != null);
 }

可以去github查看: https://github.com/haplone/spring_doc/blob/master/mvc/cors.md

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關(guān)文章

  • 使用Hutool編寫生成隨機(jī)數(shù)的工具類

    使用Hutool編寫生成隨機(jī)數(shù)的工具類

    Hutool?是一個?Java?工具類庫,提供了豐富的工具方法,其中?RandomUtil?是?Hutool?中用于生成隨機(jī)數(shù)的工具類,下面我們來看看它的具體使用吧
    2025-02-02
  • Java中的繼承與接口解讀

    Java中的繼承與接口解讀

    這篇文章主要介紹了Java中的繼承與接口使用,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • idea工具配置隱藏文件及文件夾方式

    idea工具配置隱藏文件及文件夾方式

    這篇文章主要介紹了idea工具配置隱藏文件及文件夾方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 利用Spring Boot創(chuàng)建docker image的完整步驟

    利用Spring Boot創(chuàng)建docker image的完整步驟

    這篇文章主要給大家介紹了關(guān)于如何利用Spring Boot創(chuàng)建docker image的完整步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • Spring Boot開發(fā)Web應(yīng)用詳解

    Spring Boot開發(fā)Web應(yīng)用詳解

    這篇文章主要介紹了Spring Boot開發(fā)Web應(yīng)用詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • mybatisPlus實現(xiàn)倒序拼接字符串

    mybatisPlus實現(xiàn)倒序拼接字符串

    這篇文章主要介紹了mybatisPlus實現(xiàn)倒序拼接字符串方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • 自動配置@EnableAutoConfiguration問題

    自動配置@EnableAutoConfiguration問題

    這篇文章主要介紹了自動配置@EnableAutoConfiguration問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Spring Event事件通知機(jī)制解讀

    Spring Event事件通知機(jī)制解讀

    這篇文章主要介紹了Spring Event事件通知機(jī)制解讀,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • java環(huán)境中的JDK、JVM、JRE詳細(xì)介紹

    java環(huán)境中的JDK、JVM、JRE詳細(xì)介紹

    這篇文章主要介紹了java環(huán)境中的JDK、JVM、JRE詳細(xì)介紹的相關(guān)資料,對于初學(xué)者還是有必要了解下,細(xì)致說明他們是什么,需要的朋友可以參考下
    2016-11-11
  • 深入理解Java中的克隆

    深入理解Java中的克隆

    想必大家對克隆都有耳聞,世界上第一只克隆羊多莉就是利用細(xì)胞核移植技術(shù)將哺乳動物的成年體細(xì)胞培育出新個體,甚為神奇。其實在Java中也存在克隆的概念,即實現(xiàn)對象的復(fù)制。本文將嘗試介紹一些關(guān)于Java中的克隆和一些深入的問題,希望可以幫助大家更好地了解克隆。
    2016-08-08

最新評論

连平县| 上饶县| 巴彦淖尔市| 甘洛县| 兰考县| 格尔木市| 象山县| 金溪县| 龙海市| 镇雄县| 安仁县| 新绛县| 东莞市| 禄丰县| 合川市| 上饶市| 稻城县| 乐亭县| 巴彦淖尔市| 呼图壁县| 德保县| 石城县| 镇坪县| 高清| 萝北县| 南通市| 建昌县| 合阳县| 固安县| 耿马| 洞口县| 宁远县| 安国市| 特克斯县| 永泰县| 专栏| 沙坪坝区| 鹿邑县| 商城县| 灯塔市| 景德镇市|