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

spring MVC cors跨域實現原始碼的範例程式碼解析

Mar 09, 2017 am 10:27 AM
cors mvc spring

本文主要介紹了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

熱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 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

編程新範式,當Spring Boot遇上OpenAI 編程新範式,當Spring Boot遇上OpenAI Feb 01, 2024 pm 09:18 PM

2023年,AI技術已成為熱門話題,對各行業產生了巨大影響,程式設計領域尤其如此。人們越來越認識到AI技術的重要性,Spring社群也不例外。隨著GenAI(GeneralArtificialIntelligence)技術的不斷進步,簡化具備AI功能的應用程式的創建變得至關重要和迫切。在這個背景下,"SpringAI"應運而生,旨在簡化開發AI功能應用程式的過程,使其變得簡單直觀,避免不必要的複雜性。透過"SpringAI",開發者可以更輕鬆地建立具備AI功能的應用程序,將其變得更加易於使用和操作

利用Spring Boot以及Spring AI建構生成式人工智慧應用 利用Spring Boot以及Spring AI建構生成式人工智慧應用 Apr 28, 2024 am 11:46 AM

Spring+AI作為行業領導者,透過其強大、靈活的API和先進的功能,為各種行業提供了領先性的解決方案。在本專題中,我們將深入探討Spring+AI在各領域的應用範例,每個案例都將展示Spring+AI如何滿足特定需求,實現目標,並將這些LESSONSLEARNED擴展到更廣泛的應用。希望這個專題能對你有所啟發,更深入地理解和利用Spring+AI的無限可能。 Spring框架在軟體開發領域已經有超過20年的歷史,自SpringBoot1.0版本發布以來已有10年。現在,無人會質疑,Spring

spring編程式事務有哪些實作方式 spring編程式事務有哪些實作方式 Jan 08, 2024 am 10:23 AM

spring編程式事務的實作方式:1、使用TransactionTemplate;2、使用TransactionCallback和TransactionCallbackWithoutResult;3、使用Transactional註解;4、使用TransactionTemplate和@Transactional結合使用;5、自訂事務管理器。

PHP MVC 架構:建立面向未來的 Web 應用程式 PHP MVC 架構:建立面向未來的 Web 應用程式 Mar 03, 2024 am 09:01 AM

引言在當今快速發展的數位世界中,建立健壯、靈活且可維護的WEB應用程式至關重要。 PHPmvc架構提供了實現這一目標的理想解決方案。 MVC(模型-視圖-控制器)是一種廣泛使用的設計模式,可將應用程式的各個方面分離為獨立的元件。 MVC架構的基礎MVC架構的核心原理是分離關注點:模型:封裝應用程式的資料和業務邏輯。視圖:負責呈現資料並處理使用者互動。控制器:協調模型和視圖之間的交互,管理使用者請求和業務邏輯。 PHPMVC架構phpMVC架構遵循傳統MVC模式,但也引進了語言特定的功能。以下是PHPMVC

Spring如何設定事務隔離級別 Spring如何設定事務隔離級別 Jan 26, 2024 pm 05:38 PM

Spring設定事務隔離等級的方法:1、使用@Transactional註解;2、在Spring設定檔中設定;3、使用PlatformTransactionManager;4、在Java配置類別中設定。詳細介紹:1、使用@Transactional註解,在需要進行事務管理的類別或方法上加入@Transactional註解,並在屬性中設定隔離等級;2、在Spring設定檔等等。

JUnit單元測試框架在Spring專案中的應用 JUnit單元測試框架在Spring專案中的應用 Apr 18, 2024 pm 04:54 PM

JUnit是Spring專案中廣泛使用的Java單元測試框架,可透過以下步驟應用:新增JUnit依賴項:org.junit.jupiterjunit-jupiter5.8.1test編寫測試案例:使用@ExtendWith(SpringExtension.class)啟用擴展,使用@Autowired注入Bean,使用@BeforeEach和@AfterEach準備和清理,用@Test標記測試方法。

Spring註解大揭秘:常用註解解析 Spring註解大揭秘:常用註解解析 Dec 30, 2023 am 11:28 AM

Spring是一個開源框架,提供了許多註解來簡化和增強Java開發。本文將詳細解釋常用的Spring註解,並提供具體的程式碼範例。 @Autowired:自動組裝@Autowired註解可以用於自動組裝Spring容器中的Bean。當我們在需要依賴的地方使用@Autowired註解時,Spring將會在容器中尋找匹配的Bean並自動注入。範例程式碼如下:@Auto

Spring Security權限控制框架使用指南 Spring Security權限控制框架使用指南 Feb 18, 2024 pm 05:00 PM

在後台管理系統中,通常需要存取權限控制,以限制不同使用者對介面的存取能力。如果使用者缺乏特定權限,則無法存取某些介面。本文將用waynboot-mall專案舉例,跟大家介紹常見後管系統如何引入權限控制框架SpringSecurity。大綱如下:waynboot-mall專案網址:https://github.com/wayn111/waynboot-mall一、什麼是SpringSecuritySpringSecurity是一個基於Spring框架的開源項目,旨在為Java應用程式提供強大且靈活的安

See all articles