この記事では主に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 はポジョです特にクロスドメイン構成情報をカプセル化します
関与するJavaクラス:
情報をカプセル化するpojo
CorsConfigurationリクエストとクロスドメイン設定情報を保存するコンテナ
CorsConfigurationSource、URLBasedCorsConfigurationSource
特定の処理クラス
CorsProcessor、DefaultCorsProcessorCorsUtils
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 サイトの他の関連記事を参照してください。