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!