ホームページ  >  記事  >  Java  >  Spring MVC cors クロスドメイン実装ソースコードのサンプルコード分析

Spring MVC cors クロスドメイン実装ソースコードのサンプルコード分析

黄舟
黄舟オリジナル
2017-03-09 10:27:501912ブラウズ

この記事では主にSpring MVC corsのクロスドメイン実装のソースコード解析を紹介します。非常に優れた参考値です。以下のエディターで見てみましょう

用語の説明: Cross-Origin Resource Sharing

簡単に言うと、プロトコル、IP、httpメソッドが異なっていれば、クロスドメインです。

spring MVC は 4.2 以降、クロスドメインのサポートを追加しました。

クロスドメインの具体的な定義については、mozilla にアクセスして

ユースケース

Spring MVC でクロスドメインを使用するには 3 つの方法があります:

Web で CorsFilter を構成する注釈を使用して xml で構成された .xml

<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>

// 简单配置,未配置的均使用默认值,就是全面放开
<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>

ee

関連する概念

CorsConfiguration はポジョです特にクロスドメイン構成情報をカプセル化します

  • CorsConfigurationSource リクエストとクロスドメイン構成情報をマッピングするためのコンテナ

  • 特定のクロスドメイン操作用の CorsProcessor クラス

  • Nuogan クロスドメイン構成情報初期化クラス

  • クロスドメイン使用のためのNuoganアダプター

  • 関与するJavaクラス:

情報をカプセル化するpojo

CorsConfiguration

リクエストとクロスドメイン設定情報を保存するコンテナ

CorsConfigurationSource、URLBasedCorsConfigurationSource

特定の処理クラス

CorsProcessor、DefaultCorsProcessor

CorsUtils

OncePerRequestFilterインターフェースを実装

CorsFilter

リクエストがcorsであるかどうかを確認し、対応するアダプター

AbstractHandlerMapping (内部を含む) class PreFlightHandler、CorsInterceptor

CrossOriginアノテーション情報の読み取り

AbstractHandlerMethodMapping、RequestMappingHandlerMapping

xmlファイルからのクロスドメイン構成情報の読み取り

CorsBeanDefinitionParser

クロスドメインの登録補助クラス

MvcNamespaceUtils

debug分析

必要なコードを理解するには、まずクロスドメイン情報をカプセル化する pojo を理解しましょう -- CorsConfiguration

これは、クロスドメインに対応するいくつかのプロパティに加えて、combined、checkOrigin だけです。 、checkHttpMethod、およびcheckHeaders。 属性は複数の値と組み合わせて使用​​されます。

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

combine は、クロスドメイン情報をマージすることです

3 つのチェック方法は、リクエスト内の情報が許可された範囲に含まれているかどうかをチェックすることです

設定の初期化

が、システムが開始され、構成ファイルを解析します。RequestMappingHandlerMapping をロードするときに、InitializingBean の afterProperties フックを通じて initCorsConfiguration を呼び出し、アノテーション情報を初期化します。

構成ファイルの初期化

CorsBeanDefinitionParser クラスの parse メソッドにブレークポイントを置きます。

CorsBeanDefinitionParserのコールスタック

ここでコードを通して解析を確認できます

クロスドメイン情報の構成では、パス単位で複数のマッピング関係を定義できます。

解析中に定義がない場合は、デフォルト設定が使用されます

 // 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;

解析が完了したら、MvcNamespaceUtils.registerCorsConfiguratoions を通じて登録します

ここでは Spring Bean コンテナ管理の統一プロセスに従います。 BeanDefinition に変換されてインスタンス化されます。

// 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));
  }
  // ...
 }

アノテーションの初期化

RequestMappingHandlerMappingのinitCorsConfiguration内のCrossOriginアノテーションが付けられたメソッドをスキャンして情報を抽出します。

RequestMappingHandlerMapping_initCorsConfiguration

// 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);
 }

クロスドメインリクエスト処理

HandlerMapping 検索プロセッサを正常に処理した後、AbstractHandlerMapping.getHandlerでクロスドメインリクエストであるかどうかを確認します。 2回で完了します処理:

事前リクエストの場合は、プロセッサを内部クラス 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 中国語 Web サイトの他の関連記事を参照してください。

    声明:
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。