搜尋
首頁Javajava教程spring MVC cors跨域實現原始碼的範例程式碼解析

本文主要介紹了spring MVC cors跨域實作原始碼解析。具有很好的參考價值,以下跟著小編一起來看下吧

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

簡單說就是只要協議、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中設定

// 简单配置,未配置的均使用默认值,就是全面放开
<mvc:cors> 
 <mvc:mapping path="/**" /> 
</mvc:cors> 
// 这是一个全量配置
<mvc:cors> 
 <mvc:mapping path="/api/**" 
  allowed-origins="http://domain1.com, http://www.php.cn/" 
  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 具體進行跨域操作的類別
  • #諾幹跨域配置資訊初始化類別
  • #諾幹跨域使用的Adapter
所涉及的java類:

封裝資訊的pojo

CorsConfiguration

儲存request與跨網域配置資訊的容器

#CorsConfigurationSource、UrlBasedCorsConfigurationSource

具體處理類

CorsProcessor、DefaultCorsProcessor

CorsUtils

實作OncePerRequestFilter介面的Adapter

CorsFilter

校驗request是否cors,並封裝對應的Adapter

AbstractHandlerMapping、包含內部類別PreFlightHandler、CorsInterceptor

讀取CrossOrigin註解資訊

AbstractHandlerMethodMapping、RequestMappingHandlerMapping

#從網域設定資訊

CorsBeanDefinitionParser

跨網域註冊輔助類別

MvcNamespaceUtils

debug分析

#要看懂程式碼我們需要先了解下封裝跨域資訊的pojo--CorsConfiguration

這邊是一個非常簡單的pojo,除了跨域對應的幾個屬性,就只有combine、 checkOrigin、checkHttpMethod、checkHeaders。

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

 // CorsConfiguration
 public static final String ALL = "*";
 // 允许的请求源
 private List<String> allowedOrigins;
 // 允许的http方法
 private List<String> allowedMethods;
 // 允许的请求头
 private List<String> allowedHeaders;
 // 返回的响应头
 private List<String> exposedHeaders;
 // 是否允许携带cookies
 private Boolean allowCredentials;
 // 预请求的存活有效期
 private Long maxAge;

combine是將跨域資訊合併

3個check方法分別是核對request中的資訊是否包含在允許範圍內

配置初始化在系統啟動時透過CorsBeanDefinitionParser解析設定檔;

#載入RequestMappingHandlerMapping時,透過InitializingBean的afterProperties的鉤子呼叫initCorsConfiguration初始化註解訊息;

設定檔初始化在CorsBeanDefinitionParser類別的parse方法中打一個斷點。

CorsBeanDefinitionParser的呼叫堆疊

透過程式碼可以看到這邊解析

跨域資訊的配置可以以path為單位定義多個映射關係。

解析時如果沒有定義則使用預設設定

// CorsBeanDefinitionParser
if (mappings.isEmpty()) {
 // 最简配置时的默认设置
 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容器管理的統一流程,現在轉換為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);
  // ... 设置默认值
  return config;
 }

##跨網域請求處理

#HandlerMapping在正常處理完查找處理器後,在AbstractHandlerMapping.getHandler中校驗是否是跨域請求,如果是分兩種進行處理:

如果是預請求,將處理器替換為內部類別PreFlightHandler
  • 如果是正常要求,新增CorsInterceptor攔截器

拿到处理器后,通过请求头是否包含Origin判断是否跨域,如果是跨域,通过UrlBasedCorsConfigurationSource获取跨域配置信息,并委托getCorsHandlerExecutionChain处理

UrlBasedCorsConfigurationSource是CorsConfigurationSource的实现,从类名就可以猜出这边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判断是否预请求,如果是使用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);
 }


以上是spring MVC cors跨域實現原始碼的範例程式碼解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?Mar 17, 2025 pm 05:46 PM

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)?如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)?Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?Mar 17, 2025 pm 05:44 PM

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?Mar 17, 2025 pm 05:43 PM

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?Mar 17, 2025 pm 05:35 PM

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具