Heim  >  Artikel  >  Java  >  Beispielcode-Analyse des Quellcodes für die domänenübergreifende Implementierung von Spring MVC Cors

Beispielcode-Analyse des Quellcodes für die domänenübergreifende Implementierung von Spring MVC Cors

黄舟
黄舟Original
2017-03-09 10:27:501865Durchsuche

In diesem Artikel wird hauptsächlich das Parsen des Quellcodes für die domänenübergreifende Implementierung von Spring MVC Cors vorgestellt. Es hat einen sehr guten Referenzwert. Schauen wir es uns mit dem Editor an.

Erklärung der Begriffe: Cross-Origin Resource Sharing (Cross-Origin Resource Sharing)

Um es einfach auszudrücken , solange das Protokoll, IP, Jeder Unterschied in der http-Methode ist domänenübergreifend.

Spring MVC bietet seit 4.2 domänenübergreifende Unterstützung.

Für die spezifische Definition von domänenübergreifend gehen Sie bitte zu Mozilla, um

Anwendungsfälle

Es gibt 3 domänenübergreifende Verwendung in Spring MVC Methode:

CorsFilter in web.xml konfigurieren

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

Konfigurieren

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

in XML mit Anmerkungen

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

Beteiligte Konzepte

  • CorsConfiguration-spezifisches Paket Das Pojo der domänenübergreifenden Konfigurationsinformationen

  • CorsConfigurationSource-Anfrage und der Container zum Zuordnen domänenübergreifender Konfigurationsinformationen

  • CorsProcessor-Klasse der speziell domänenübergreifende Vorgänge ausführt

  • Nogan Cross-Domain Configuration Information Initialization Class

  • Nogan Cross-Domain Adapter

Beteiligte Java-Klassen:

Pojo, das Informationen kapselt

CorsConfiguration

das speichert Anforderungs- und domänenübergreifende Konfigurationsinformationen Container

CorsConfigurationSource, UrlBasedCorsConfigurationSource

Spezifische Verarbeitungsklasse

CorsProcessor, DefaultCorsProcessor

CorsUtils

Implementieren Sie den OncePerRequestFilter-Schnittstellenadapter

CorsFilter

Überprüfen Sie, ob die Anfrage cors ist, und kapseln Sie sie entsprechender Adapter

AbstractHandlerMapping, einschließlich interner Klassen PreFlightHandler, CorsInterceptor

CrossOrigin-Annotationsinformationen lesen

AbstractHandlerMethodMapping, RequestMappingHandlerMapping

aus XML-Datei Informationen zur domänenübergreifenden Konfiguration lesen

CorsBeanDefinitionParser

Hilfsklasse für domänenübergreifende Registrierung

MvcNamespaceUtils

Debug-Analyse

Um den Code zu verstehen, müssen wir zuerst das Pojo verstehen, das domänenübergreifende Informationen kapselt – CorsConfiguration

Dies ist ein sehr einfaches Pojo, Mit Ausnahme einiger domänenübergreifender Korrespondenzen sind die Attribute nur „combine“, „checkOrigin“, „checkHttpMethod“ und „checkHeaders“.

Attribute werden in Kombination mit mehreren Werten verwendet.

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

Kombinieren dient zum Zusammenführen domänenübergreifender Informationen

Mit den drei Prüfmethoden wird überprüft, ob die Informationen in der Anfrage enthalten sind im zulässigen Rahmen

Konfigurationsinitialisierung

analysiert die Konfigurationsdatei beim Systemstart über CorsBeanDefinitionParser

; Laden von RequestMappingHandlerMapping über InitializingBean. Der AfterProperties-Hook ruft initCorsConfiguration auf, um die Anmerkungsinformationen zu initialisieren.

Initialisierung der Konfigurationsdatei

Setzen Sie einen Haltepunkt in die Parse-Methode der CorsBeanDefinitionParser-Klasse.

Der Aufrufstapel von CorsBeanDefinitionParser

Sie können die Analyse hier durch den Code sehen

Domänenübergreifend Die Konfiguration von Informationen kann mehrere Zuordnungsbeziehungen in Pfadeinheiten definieren.

Wenn beim Parsen keine Definition vorhanden ist, werden die Standardeinstellungen verwendet

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

Registrieren Sie sich nach Abschluss des Parsens über MvcNamespaceUtils. registerCorsConfiguratoions

Was wir hier verfolgen, ist der einheitliche Prozess der Spring-Bean-Container-Verwaltung, der nun in BeanDefinition konvertiert und dann instanziiert wird.

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

Annotationsinitialisierung

Scannen Sie die mit CrossOrigin annotierte Methode in der initCorsConfiguration von RequestMappingHandlerMapping und extrahieren Sie die Informationen .

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

Cross-Origin-Anfragebearbeitung

Nachdem der Suchprozessor normal verarbeitet wurde, prüft HandlerMapping, ob es sich um eine domänenübergreifende Anfrage in AbstractHandlerMapping.getHandler handelt. Wenn es sich um eine domänenübergreifende Anfrage handelt, wird sie auf zwei Arten verarbeitet:

  • Wenn es sich um eine Vorabanfrage handelt, ersetzen Sie den Prozessor durch die interne Klasse PreFlightHandler

  • Wenn es sich um eine normale Anfrage handelt, fügen Sie den CorsInterceptor-Interceptor hinzu

拿到处理器后,通过请求头是否包含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);
 }


Das obige ist der detaillierte Inhalt vonBeispielcode-Analyse des Quellcodes für die domänenübergreifende Implementierung von Spring MVC Cors. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn