Spring@CrossOrigin注解原理实现
现实开发中,我们难免遇到跨域问题,以前笔者只知道jsonp这种解决⽅式,后⾯听说spring只要加⼊@CrossOrigin即可解决跨域问题。本着好奇的⼼⾥,笔者看了下@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 = BeanType();
//获取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);
springboot原理图解
updateCorsConfig(config, methodAnnotation);
if (CollectionUtils.AllowedMethods())) {
for (RequestMethod allowedMethod : MethodsCondition().getMethods()) {
config.addAllowedMethod(allowedMethod.name());
}
}
return config.applyPermitDefaultValues();
}
将结果返回到了AbstractHandlerMethodMapping#register,主要代码如下
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
//会保存handlerMethod处理跨域请求的配置
}
当⼀个跨域请求过来时,spring在获取handler时会判断这个请求是否是⼀个跨域请求,如果是,则会返回⼀个可以处理跨域的handler
AbstractHandlerMapping#getHandler
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
//如果是⼀个跨域请求
if (CorsUtils.isCorsRequest(request)) {
//拿到跨域的全局配置
CorsConfiguration globalConfig = CorsConfiguration(request);
//拿到hander的跨域配置
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfigbine(handlerConfig) : handlerConfig);
//处理跨域(即往响应头添加Access-Control-Allow-Origin信息等),并返回对应的handler对象
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
我们可以看下如何判定⼀个请求是⼀个跨域请求,
public static boolean isCorsRequest(HttpServletRequest request) {
//判定请求头是否有Origin 属性即可
return (Header(HttpHeaders.ORIGIN) != null);
再看下getCorsHandlerExecutionChain 是如何获取⼀个handler
protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
if (CorsUtils.isPreFlightRequest(request)) {
HandlerInterceptor[] interceptors = Interceptors();
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 = Headers().getOrigin();
String allowOrigin = checkOrigin(config, requestOrigin);
HttpHeaders responseHeaders = Headers();
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.ExposedHeaders())) {
responseHeaders.ExposedHeaders());
if (Boolean.TRUE.AllowCredentials())) {
responseHeaders.setAccessControlAllowCredentials(true);
}
if (preFlightRequest && MaxAge() != null) {
responseHeaders.MaxAge());
}
//刷新
response.flush();
return true;
}
⾄此@CrossOrigin的使命就完成了,说⽩了就是⽤给response添加响应头信息⽽已
到此这篇关于Spring @CrossOrigin 注解原理实现的⽂章就介绍到这了,更多相关Spring @CrossOrigin 注解内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。