博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
全局跨域配置引起的问题
阅读量:6683 次
发布时间:2019-06-25

本文共 5703 字,大约阅读时间需要 19 分钟。

hot3.png

先说问题,如果你已经知道怎么处理,请忽略。

项目中有个全局跨域配置,正常请求前端不会有跨域问题,如果在拦截器中抛出错误,前端就会有问题。

原因分析

既然在拦截器中报错,就有跨域问题,那就说明这个跨域配置并没有起作用。

当时的跨域配置是如下这样

@Configurationpublic class CorsConfiguration implements WebMvcConfigurer {    @Override    public void addCorsMappings(CorsRegistry registry) {        registry.addMapping("/**")                .allowedOrigins("*")                .allowCredentials(true)                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")                .maxAge(3600);    }}

那么这个配置为什么没起作用呢?我们先来看一张图。

d0e4d612c4c31d36cf13a5957547e26e749.jpg

这是Spring MVC的流程图,用户所有的请求都会经过DispatcherServlet,我们从DispatcherServlet这里开始分析,找到doDispatch方法,他上面有这样的注释。

/**

* Process the actual dispatching to the handler.
*

The handler will be obtained by applying the servlet's HandlerMappings in order.

* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
*

All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers

* themselves to decide which methods are acceptable.
* request current HTTP request
* response current HTTP response
* Exception in case of any kind of processing failure
*/

注意其中的All HTTP methods are handled by this method

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {        //...省略                // Determine handler for the current request.                mappedHandler = getHandler(processedRequest);                if (mappedHandler == null) {                    noHandlerFound(processedRequest, response);                    return;                }       //...省略      }

看这个方法,其他都忽略,只看getHandler(processedRequest),它会获得HandlerExecutionChain 拦截器责任链,进入该方法

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        if (this.handlerMappings != null) {            for (HandlerMapping hm : this.handlerMappings) {                if (logger.isTraceEnabled()) {                    logger.trace(                            "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");                }                HandlerExecutionChain handler = hm.getHandler(request);                if (handler != null) {                    return handler;                }            }        }        return null;    }

这只是一个循环,里面还有一个getHandler(request)方法,进入该方法

    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {        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 = obtainApplicationContext().getBean(handlerName);        }        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);           //获取新的HandlerExecutionChain            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);        }        return executionChain;    }

代码变多了,在这里看到了我们需要的CorsConfiguration,把注意集中在这部分,进入getCorsHandlerExecutionChain(request, executionChain, config)看看

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

这里首先会判断请求是否是预检请求,预检请求是OPTIONS方法,用于检查服务器是否接受前端过来的请求

不是预检请求走else,这里将跨域配置组装成了一个Interceptor并加入到拦截器责任链中,进入addInterceptor方法中

    public void addInterceptor(HandlerInterceptor interceptor) {        initInterceptorList().add(interceptor);    }   //initInterceptorList()    private List
 initInterceptorList() {        if (this.interceptorList == null) {            this.interceptorList = new ArrayList<>();            if (this.interceptors != null) {                // An interceptor array specified through the constructor                CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);            }        }        this.interceptors = null;        return this.interceptorList;    }

你会发现initInterceptorList()其实就是一个List,而我们的跨域拦截器就被放入List的最后一个位置

因此你在自定义拦截器中抛出错误,是并不会执行到跨域拦截器的,而是直接返回了。

解决方案

使用Filter过滤器来处理跨域请求,修改后的配置

@Configurationpublic class CorsConfig{    @Bean    public CorsFilter corsFilter() {        CorsConfiguration config = new CorsConfiguration();        config.addAllowedOrigin("*");        config.setAllowCredentials(true);        config.addAllowedMethod("*");        config.addAllowedHeader("*");        config.setMaxAge(3600L);        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();        configSource.registerCorsConfiguration("/**", config);        return new CorsFilter(configSource);    }}

使用Filter就可以是因为,Filter在Servlet前后起作用,和执行顺序

下面是两者的区别

5ba9d33af117e31dc7fdadf2bb8d529112a.jpg

执行顺序
过滤器前->拦截器前->Action处理->拦截器后->过滤器后

 

49b711889af2674b3abe0bdb3890d7f62b4.jpg

转载于:https://my.oschina.net/itsaysay/blog/3011831

你可能感兴趣的文章
android view知识点 总结
查看>>
记一个鼠标略过时候的css动画
查看>>
HTTP协议
查看>>
slave->pxc后GTID不一致
查看>>
WPF 与Surface 2.0 SDK 亲密接触 - 图形缩放篇
查看>>
PhotoShop常用的功能汇总
查看>>
基于移动端Reactive Native轮播组件的应用与开发详解
查看>>
专家的修炼之路 —— 德雷福斯模型 Dreyfus
查看>>
TcpClient和TcpListener 类的使用-编写一个点对点聊天工具(初级入门篇)
查看>>
CSS 圆角
查看>>
C#解压缩文件
查看>>
Golang学习笔记——Slice
查看>>
ERDAS软件应用(三)遥感图像的拼接
查看>>
在面试中如何展示虚拟机和内存调优技能
查看>>
hdu 1534 Schedule Problem (差分约束)
查看>>
HDU1159 Common Subsequence【最长公共子序列】
查看>>
C++ 字符数组函数与string函数
查看>>
无限极分类优化方式
查看>>
从运维的角度理解Iaas、Paas、Saas云计算
查看>>
使用动画播放文件夹中的图片
查看>>