java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot中addCorsMappings配置跨域与拦截器互斥

解读SpringBoot中addCorsMappings配置跨域与拦截器互斥问题的原因

作者:huangyaa729

这篇文章主要介绍了解读SpringBoot中addCorsMappings配置跨域与拦截器互斥问题的原因,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

SpringBoot中addCorsMappings配置跨域与拦截器互斥

如题,前两天在做前后端分离项目时,碰到了这个问题,登录token验证的拦截器使项目中配置的跨域配置失效,导致浏览器抛出跨域请求错误,跨域配置如下:

public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                        registry.addMapping("/**")
                        .allowedOrigins(origins)
                        .allowedHeaders("*")
                        .allowCredentials(true)
                        .allowedMethods("*")
                        .maxAge(3600);
            }
        };
    }

通过在网上的查询,发现了如下解释

然后参考了网上给出的方法,重新引入了跨域过滤器配置,解决了这个问题。

那最终这个问题产生的原因是什么的,真的如上诉所说吗,我通过调试与研究源码,找了原因。

在springMvc中,我们都知道路径的映射匹配是通过DispatcherServlet这个类来实现的,最终的函数执行在doDispatch()这个方法中:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request. 
		(1)mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
		(2)HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

		(3)if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
		(4)mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
		(5)mappedHandler.applyPostHandle(processedRequest, response, mv);
			}

在这个类中,我们关注(1)-(5)这几句代码,基本上整个映射执行的逻辑就明了了:

在这个函数中我们并没有看到什么时候执行addCorsMappings这一配置内容,那它到底是什么时候添加的呢,那就需要仔细分析步骤(1)了:获取整个执行器链。

通过调试定位,我发现getHandle()最终执行的AbstractHandlerMapping这个类的函数

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
(1)Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);
		}

(2)HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
(3)	executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}

这个函数中我也标记了(1)、(2)、(3)这三条语句:

进入这个方法后,一切都明了了;

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

先判断request是否是预检请求(不明白什么是预检请求的可以自身搜索相关解释,很多,不再赘述),是预检请求则生成个预检执行器PreFlightHandler,然后在doDispatch函数(4)中执行;

否则生成一个跨域拦截器加入拦截器链中,最终再doDispatch函数(3)处执行,而因为拦截器是顺序执行的,如果前面执行失败异常返回后,后面的则不再执行。

所以当跨越请求在拦截器那边处理后就异常返回了,那么响应的response报文头部关于跨域允许的信息就没有被正确设置,导致浏览器认为服务不允许跨域,而造成错误;而当我们使用过滤器时,过滤器先于拦截器执行,那么无论是否被拦截,始终有允许跨域的头部信息,就不会出问题了。

另注:

对于预检请求,一般token验证时是不会拦截此请求的,因为预检请求不会附带任何参数信息,也就没有所需的token信息,所以拦截时需过滤预检请求

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文