小編給大家分享一下Spring @CrossOrigin注解原理是什么,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!
先說原理:其實很簡單,就是利用spring的攔截器實現往response里添加 Access-Control-Allow-Origin等響應頭信息,我們可以看下spring是怎么做的
注:這里使用的spring版本為5.0.6
我們可以先往RequestMappingHandlerMapping 的initCorsConfiguration方法打一個斷點,發現方法調用情況如下

如果controller在類上標了@CrossOrigin或在方法上標了@CrossOrigin注解,則spring 在記錄mapper映射時會記錄對應跨域請求映射,代碼如下
RequestMappingHandlerMapping
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
Class<?> beanType = handlerMethod.getBeanType();
//獲取handler上的CrossOrigin 注解
CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
//獲取handler 方法上的CrossOrigin 注解
CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
if (typeAnnotation == null && methodAnnotation == null) {
//如果類上和方法都沒標CrossOrigin 注解,則返回一個null
return null;
}
//構建一個CorsConfiguration 并返回
CorsConfiguration config = new CorsConfiguration();
updateCorsConfig(config, typeAnnotation);
updateCorsConfig(config, methodAnnotation);
if (CollectionUtils.isEmpty(config.getAllowedMethods())) {
for (RequestMethod allowedMethod : mappingInfo.getMethodsCondition().getMethods()) {
config.addAllowedMethod(allowedMethod.name());
}
}
return config.applyPermitDefaultValues();
}將結果返回到了AbstractHandlerMethodMapping#register,主要代碼如下
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
//會保存handlerMethod處理跨域請求的配置
this.corsLookup.put(handlerMethod, corsConfig);
}當一個跨域請求過來時,spring在獲取handler時會判斷這個請求是否是一個跨域請求,如果是,則會返回一個可以處理跨域的handler
AbstractHandlerMapping#getHandler
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//如果是一個跨域請求
if (CorsUtils.isCorsRequest(request)) {
//拿到跨域的全局配置
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
//拿到hander的跨域配置
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
//處理跨域(即往響應頭添加Access-Control-Allow-Origin信息等),并返回對應的handler對象
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}我們可以看下如何判定一個請求是一個跨域請求,
public static boolean isCorsRequest(HttpServletRequest request) {
//判定請求頭是否有Origin 屬性即可
return (request.getHeader(HttpHeaders.ORIGIN) != null);
}再看下getCorsHandlerExecutionChain 是如何獲取一個handler
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, @Nullable 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;
}也就是在調用目標方法前會先調用CorsInterceptor#preHandle,我們觀察得到其也是調用了corsProcessor.processRequest方法,我們往這里打個斷點
processRequest方法的主要邏輯如下
public boolean processRequest(@Nullable CorsConfiguration config, HttpServletRequest request,
HttpServletResponse response) throws IOException {
//....
//調用了自身的handleInternal方法
return handleInternal(serverRequest, serverResponse, config, preFlightRequest);
}
protected boolean handleInternal(ServerHttpRequest request, ServerHttpResponse response,
CorsConfiguration config, boolean preFlightRequest) throws IOException {
String requestOrigin = request.getHeaders().getOrigin();
String allowOrigin = checkOrigin(config, requestOrigin);
HttpHeaders responseHeaders = response.getHeaders();
responseHeaders.addAll(HttpHeaders.VARY, Arrays.asList(HttpHeaders.ORIGIN,
HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, HttpHeaders.ACCESS_CONTROL_REQUEST_HEADERS));
if (allowOrigin == null) {
logger.debug("Rejecting CORS request because '" + requestOrigin + "' origin is not allowed");
rejectRequest(response);
return false;
}
HttpMethod requestMethod = getMethodToUse(request, preFlightRequest);
List<HttpMethod> allowMethods = checkMethods(config, requestMethod);
if (allowMethods == null) {
logger.debug("Rejecting CORS request because '" + requestMethod + "' request method is not allowed");
rejectRequest(response);
return false;
}
List<String> requestHeaders = getHeadersToUse(request, preFlightRequest);
List<String> allowHeaders = checkHeaders(config, requestHeaders);
if (preFlightRequest && allowHeaders == null) {
logger.debug("Rejecting CORS request because '" + requestHeaders + "' request headers are not allowed");
rejectRequest(response);
return false;
}
//設置響應頭
responseHeaders.setAccessControlAllowOrigin(allowOrigin);
if (preFlightRequest) {
responseHeaders.setAccessControlAllowMethods(allowMethods);
}
if (preFlightRequest && !allowHeaders.isEmpty()) {
responseHeaders.setAccessControlAllowHeaders(allowHeaders);
}
if (!CollectionUtils.isEmpty(config.getExposedHeaders())) {
responseHeaders.setAccessControlExposeHeaders(config.getExposedHeaders());
}
if (Boolean.TRUE.equals(config.getAllowCredentials())) {
responseHeaders.setAccessControlAllowCredentials(true);
}
if (preFlightRequest && config.getMaxAge() != null) {
responseHeaders.setAccessControlMaxAge(config.getMaxAge());
}
//刷新
response.flush();
return true;
}看完了這篇文章,相信你對Spring @CrossOrigin注解原理是什么有了一定的了解,想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。