SpringBoot在使用SpringSecurity时,配置跨域过滤器CorsFilter不生效分析解决

这篇具有很好参考价值的文章主要介绍了SpringBoot在使用SpringSecurity时,配置跨域过滤器CorsFilter不生效分析解决。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

SpringBoot在使用SpringSecurity时配置跨域过滤器CorsFilter不生效

  • 此文中代码只粘贴部分代码,完整版请自行查看
  • 请求一般为重启debug服务再次请求

1.配置

一般配置方法(适用于没有SpringSecurity配置时)


@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        List<String> list = Arrays.asList("*");
        corsConfiguration.setAllowedHeaders(list);
        corsConfiguration.setAllowedMethods(list);
        corsConfiguration.setAllowedOrigins(list);
        source.registerCorsConfiguration("/**", corsConfiguration);
        CorsFilter corsFilter = new CorsFilter(source);
        return corsFilter;
    }
}

SpringSecurity配置时配置方法


@Configuration
public class CorsConfig {
    @Bean
    public FilterRegistrationBean corsFilterBean() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        List<String> list = Arrays.asList("*");
        corsConfiguration.setAllowedHeaders(list);
        corsConfiguration.setAllowedMethods(list);
        corsConfiguration.setAllowedOrigins(list);
        source.registerCorsConfiguration("/**", corsConfiguration);
        CorsFilter corsFilter = new CorsFilter(source);
        FilterRegistrationBean<CorsFilter> filterRegistrationBean = new FilterRegistrationBean<>(corsFilter);
        filterRegistrationBean.setOrder(-101);
        return filterRegistrationBean;
    }
}

2.原因分析(过滤器有加载顺序)

首先说明Spring中过滤器加载使用的是FilterRegistrationBean配置过滤器,FilterRegistrationBean实现了Ordered接口,order排序值默认值为
Ordered.LOWEST_PRECEDENCE=Integer.MAX_VALUE

一般配置时的问题

  • 如下代码为SpringSecurity的过滤器注册bean

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class})
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
    @Bean
    @ConditionalOnBean(name = DEFAULT_FILTER_NAME)
    public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
            SecurityProperties securityProperties) {
        DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
                DEFAULT_FILTER_NAME);
        registration.setOrder(securityProperties.getFilter().getOrder());
        registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
        return registration;
    }
}

代码中DEFAULT_FILTER_NAME=“springSecurityFilterChain”,securityProperties.getFilter().getOrder()=-100

  • CorsFilter的过滤器注册bean由Spring生成,而生成的order=(Ordered.LOWEST_PRECEDENCE=Integer.MAX_VALUE),且优先按照Ordered接口排序,
    @Order注解在没有实现Ordered接口时才生效

3.问题查找过程

a.检查CorsFilter是否执行

查看CorsFilter类可以知道跨域逻辑主要在以下doFilterInternal()方法执行,在此方法添加断点,debug执行,发送请求时未执行此方法,由此判断CorsFilter未执行过滤逻辑

public class CorsFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
        boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
        if (!isValid || CorsUtils.isPreFlightRequest(request)) {
            return;
        }
        filterChain.doFilter(request, response);
    }
}

b.检查Filter链

找到FilterChain接口,其中有如下doFilter()方法,在此方法添加断点

public interface FilterChain {
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;
}

请求时断点停顿处为ApplicationFilterChain,在此类中找到属性ApplicationFilterConfig[] filters,查看filters的值如图
corsfilter,Spring,spring boot,java,spring,spring cloud,servlet

ApplicationFilterChain的方法internalDoFilter()逻辑可以看出filters执行顺序为数组下标升序,而corsFilter位于springSecurityFilterChain的后面,
当执行到springSecurityFilterChain时,springSecurityFilterChain过滤器内部会进行身份校验获取用户信息,如果是跨域请求,用户验证信息会被浏览器拦截,直接中断过滤器执行,
而corsFilter的跨域配置还没有执行到,所以需要配置corsFilter的执行顺序在springSecurityFilterChain之前

public final class ApplicationFilterChain implements FilterChain {
    private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
            throws IOException, ServletException {
        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                }
                if (Globals.IS_SECURITY_ENABLED) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                            ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
                } else {
                    filter.doFilter(request, response, this);
                }
            } catch (IOException | ServletException | RuntimeException e) {
                throw e;
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                throw new ServletException(sm.getString("filterChain.filter"), e);
            }
            return;
        }
    }
}

c.分析filter链的生成过程

ApplicationFilterChain中filters过滤连的赋值操作,在addFilter()方法中进行,在此方法中添加断点

public final class ApplicationFilterChain implements FilterChain {
    void addFilter(ApplicationFilterConfig filterConfig) {
        filters[n++] = filterConfig;
    }
}

请求时可得到如下调用栈
corsfilter,Spring,spring boot,java,spring,spring cloud,servlet

查看ApplicationFilterFactory类的方法createFilterChain()可知,ApplicationFilterConfig由FilterMap生成,而FilterMaps[]
由context(TomcatEmbeddedContext)的findFilterMaps()获取到

public final class ApplicationFilterFactory {
    public static ApplicationFilterChain createFilterChain(ServletRequest request,
                                                           Wrapper wrapper, Servlet servlet) {
        // Acquire the filter mappings for this Context
        StandardContext context = (StandardContext) wrapper.getParent();
        FilterMap filterMaps[] = context.findFilterMaps();

        // If there are no filter mappings, we are done
        if ((filterMaps == null) || (filterMaps.length == 0))
            return filterChain;

        // Acquire the information we will need to match filter mappings
        DispatcherType dispatcher =
                (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

        String requestPath = null;
        Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
        if (attribute != null) {
            requestPath = attribute.toString();
        }

        String servletName = wrapper.getName();
        for (FilterMap filterMap : filterMaps) {
            if (!matchDispatcher(filterMap, dispatcher)) {
                continue;
            }
            if (!matchFiltersURL(filterMap, requestPath))
                continue;
            ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMap.getFilterName());
            if (filterConfig == null) {
                // FIXME - log configuration problem
                continue;
            }
            filterChain.addFilter(filterConfig);
        }
    }
}

进入findFilterMaps()(此方法为TomcatEmbeddedContext继承自StandardContext的)处在StandardContext类中,分析可知FilterMap[]
由filterMaps生成,而filterMaps由addFilterMapBefore()和addFilterMap()方法添加,在两个方法添加断点

public class StandardContext extends ContainerBase implements Context, NotificationEmitter {
    @Override
    public FilterMap[] findFilterMaps() {
        return filterMaps.asArray();
    }

    @Override
    public void addFilterMapBefore(FilterMap filterMap) {
        validateFilterMap(filterMap);
        // Add this filter mapping to our registered set
        filterMaps.addBefore(filterMap);
        fireContainerEvent("addFilterMap", filterMap);
    }

    @Override
    public void addFilterMap(FilterMap filterMap) {
        validateFilterMap(filterMap);
        // Add this filter mapping to our registered set
        filterMaps.add(filterMap);
        fireContainerEvent("addFilterMap", filterMap);
    }
}

请求时断点停顿处,其调用栈如下
corsfilter,Spring,spring boot,java,spring,spring cloud,servlet

进入addMappingForUrlPatterns()方法,分析FilterMap由filterDef生成,而filterDef由setInitParameter()方法和ApplicationFilterRegistration()
构造方法控制,在方法添加断点

public class ApplicationFilterRegistration
        implements FilterRegistration.Dynamic {

    public ApplicationFilterRegistration(FilterDef filterDef,
                                         Context context) {
        this.filterDef = filterDef;
        this.context = context;

    }

    @Override
    public void addMappingForUrlPatterns(
            EnumSet<DispatcherType> dispatcherTypes, boolean isMatchAfter,
            String... urlPatterns) {

        FilterMap filterMap = new FilterMap();

        filterMap.setFilterName(filterDef.getFilterName());

        if (dispatcherTypes != null) {
            for (DispatcherType dispatcherType : dispatcherTypes) {
                filterMap.setDispatcher(dispatcherType.name());
            }
        }

        if (urlPatterns != null) {
            // % decoded (if necessary) using UTF-8
            for (String urlPattern : urlPatterns) {
                filterMap.addURLPattern(urlPattern);
            }

            if (isMatchAfter) {
                context.addFilterMap(filterMap);
            } else {
                context.addFilterMapBefore(filterMap);
            }
        }
        // else error?

    }


    @Override
    public boolean setInitParameter(String name, String value) {
        if (name == null || value == null) {
            throw new IllegalArgumentException(
                    sm.getString("applicationFilterRegistration.nullInitParam",
                            name, value));
        }
        if (getInitParameter(name) != null) {
            return false;
        }

        filterDef.addInitParameter(name, value);

        return true;
    }
}

请求时断点停顿,调用栈为
corsfilter,Spring,spring boot,java,spring,spring cloud,servlet

进入addFilter()方法,使用evaluate expression计算器分析context.findFilterDef(filterName)为null,filterDef为filterName赋值

public class ApplicationContext implements ServletContext {

    private FilterRegistration.Dynamic addFilter(String filterName,
                                                 String filterClass, Filter filter) throws IllegalStateException {

        FilterDef filterDef = context.findFilterDef(filterName);

        // Assume a 'complete' FilterRegistration is one that has a class and
        // a name
        if (filterDef == null) {
            filterDef = new FilterDef();
            filterDef.setFilterName(filterName);
            context.addFilterDef(filterDef);
        } else {
            if (filterDef.getFilterName() != null &&
                    filterDef.getFilterClass() != null) {
                return null;
            }
        }


        return new ApplicationFilterRegistration(filterDef, context);
    }
}

再次查看调用栈,分析可知filterName来源于addRegistration()方法
corsfilter,Spring,spring boot,java,spring,spring cloud,servlet

public abstract class AbstractFilterRegistrationBean<T extends Filter> extends DynamicRegistrationBean<Dynamic> {

    @Override
    protected Dynamic addRegistration(String description, ServletContext servletContext) {
        Filter filter = getFilter();
        return servletContext.addFilter(getOrDeduceName(filter), filter);
    }
}

进入addRegistration()方法分析知道filter来自getFilter()
,且this类型为FilterRegistrationBean,进入FilterRegistrationBean的getFilter()方法,返回为this.filter,查找filter的赋值操作setFilter()
方法和FilterRegistrationBean()构造方法,添加断点

public class FilterRegistrationBean<T extends Filter> extends AbstractFilterRegistrationBean<T> {

    @Override
    public T getFilter() {
        return this.filter;
    }

    public FilterRegistrationBean(T filter, ServletRegistrationBean<?>... servletRegistrationBeans) {
        super(servletRegistrationBeans);
        Assert.notNull(filter, "Filter must not be null");
        this.filter = filter;
    }

    public void setFilter(T filter) {
        Assert.notNull(filter, "Filter must not be null");
        this.filter = filter;
    }
}

请求时断点停顿,分析调用栈找到FilterRegistrationBean的filter赋值操作的源头为addAsRegistrationBean()方法
corsfilter,Spring,spring boot,java,spring,spring cloud,servlet

进入addAsRegistrationBean()方法,可以看到RegistrationBean的创建,并且设置排序属性值,最后将FilterRegistrationBean存入this.initializers

public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {

    @SafeVarargs
    public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
                                          Class<? extends ServletContextInitializer>... initializerTypes) {
        this.initializers = new LinkedMultiValueMap<>();
        this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
                : Collections.singletonList(ServletContextInitializer.class);
        addServletContextInitializerBeans(beanFactory);
        addAdaptableBeans(beanFactory);
        List<ServletContextInitializer> sortedInitializers = this.initializers.values().stream()
                .flatMap((value) -> value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
                .collect(Collectors.toList());
        this.sortedList = Collections.unmodifiableList(sortedInitializers);
        logMappings(this.initializers);
    }

    private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
                                                        Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
        List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
        for (Entry<String, B> entry : entries) {
            String beanName = entry.getKey();
            B bean = entry.getValue();
            if (this.seen.add(bean)) {
                // One that we haven't already seen
                RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
                int order = getOrder(bean);
                registration.setOrder(order);
                this.initializers.add(type, registration);
                if (logger.isTraceEnabled()) {
                    logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
                            + order + ", resource=" + getResourceDescription(beanName, beanFactory));
                }
            }
        }
    }
}

分析this.initializers的操作,没有直接取值的操作,但在ServletContextInitializerBeans()构造函数中间接赋值给了this.sortedList,而查看调用栈也可以得知
corsfilter,Spring,spring boot,java,spring,spring cloud,servlet

而this.initializers向this.sortedList赋值时最重要操作是如下代码:
将所有AbstractFilterRegistrationBean按照AnnotationAwareOrderComparator的规则进行排序,因为此类型实现了Ordered接口所以优先按照接口排序值排序,当没有实现Ordered接口时才按照@Order注解排序,
然后转换为不可修改的集合赋值给this.sortedList

List<ServletContextInitializer> sortedInitializers=this.initializers.values().stream()
        .flatMap((value)->value.stream().sorted(AnnotationAwareOrderComparator.INSTANCE))
        .collect(Collectors.toList());
this.sortedList=Collections.unmodifiableList(sortedInitializers);

在ServletContextInitializerBeans类中找到this.sortedList的操作,iterator()方法添加断点

public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer> {

    @Override
    public Iterator<ServletContextInitializer> iterator() {
        return this.sortedList.iterator();
    }
}

放开上一个断点,进入iterator(),查看调用栈
corsfilter,Spring,spring boot,java,spring,spring cloud,servlet

在selfInitialize()方法内getServletContextInitializerBeans()获取到this.sortedList,即排序后的FilterRegistrationBean,按照顺序挨个执行文章来源地址https://www.toymoban.com/news/detail-808317.html

public class ServletWebServerApplicationContext extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {

    private void selfInitialize(ServletContext servletContext) throws ServletException {
        prepareWebApplicationContext(servletContext);
        registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
            beans.onStartup(servletContext);
        }
    }
}

d.完

到了这里,关于SpringBoot在使用SpringSecurity时,配置跨域过滤器CorsFilter不生效分析解决的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • springboot中使用filter过滤器

    filter过滤器其实是JavaEE中的规范,JavaWeb中的三大组件是filter过滤器、listener监听器,servlet服务。 过滤器的作用就是把请求拦截下来,从而对请求进行一些特殊操作,比如检验用户是否登录,判断权限,设置编码格式、敏感字符处理等。 filter过滤器中有三个方法: 分别是初始

    2024年02月08日
    浏览(37)
  • Java过滤器配置类导致跨域问题:Request header field token is not allowed by Access-Control-Allow-Headers in prefli

    前端:Vue、Axios 后端:Spring Security、SpringBoot、JWT等等 报错信息如下(前端): 报错信息翻译: 在preflight响应中,Access-Control-Allow-Headers不允许请求报头字段标记。 这个翻译也让我们很快知道了问题的所在,就是Access-Control-Allow-Headers里面不允许token字段出现,也就是说,tok

    2024年02月04日
    浏览(29)
  • Springboot中使用拦截器、过滤器、监听器

    Javaweb三大组件:servlet、Filter(过滤器)、 Listener(监听器) SpringBoot特有组件:Interceptor(拦截器) 过滤器、拦截器、监听器、AOP(后续文章介绍)、全局异常处理器(后续文章介绍)是搭建系统框架时,经常用到的部分,全局异常处理器的作用很明显,就是处理接口执行

    2024年02月03日
    浏览(28)
  • 【Spring Cloud】深入探索统一网关 Gateway 的搭建,断言工厂,过滤器工厂,全局过滤器以及跨域问题

    在微服务架构中,网关是至关重要的组件,具有多重职责,为整个系统提供了一系列关键功能。从下面的微服务结构图中,我们可以明确网关的几项主要作用: 微服务结构图: 请求过滤与安全: 用户的所有请求首先经过网关,这使得网关成为系统的第一道防线。通过对传入

    2024年02月07日
    浏览(42)
  • Springboot 在 redis 中使用 BloomFilter 布隆过滤器机制

    在 pom.xml 文件中,引入Spring Boot和Redis相关依赖 创建一个布隆过滤器配置类 BloomFilterConfig : 创建一个BloomFilterController。使用布隆过滤器判断数据是否存在,从而避免缓存穿透: 向里面添加元素  获取元素

    2024年02月13日
    浏览(28)
  • Springboot 在 redis 中使用 Guava 布隆过滤器机制

    在 pom.xml 文件中,引入Spring Boot和Redis相关依赖 创建一个布隆过滤器配置类 BloomFilterConfig : 创建一个BloomFilterController。使用布隆过滤器判断数据是否存在,从而避免缓存穿透: 向里面添加元素  获取元素

    2024年02月12日
    浏览(27)
  • 【微服务技术二】Feign、Gateway(路由、过滤器、跨域)的初步认知

    之前远程调用使用RestTemplate,代码如下: 使用RestTemplate的缺陷: 代码可读性差,编程体验不统一(莫名的url路径) 参数复杂时的URL难以维护 Feign是一个声明式的http客户端,官方网址: https://github.com/OpenFeign/feign Feign替代RestTemplate 使用Feign的步骤: 1、引入依赖: 2、在消费者

    2024年02月12日
    浏览(27)
  • SpringBoot 统计API接口用时该使用过滤器还是拦截器?

    统计请求的处理时间(用时)既可以使用 Servlet 过滤器(Filter) ,也可以使用 Spring 拦截器(Interceptor) 。两者都可以在请求处理前后插入自定义逻辑,从而实现对请求响应时间的统计。 如果你需要在更底层、与框架无关的地方记录所有请求(包括静态资源请求)的处理时间

    2024年01月18日
    浏览(30)
  • springboot中过滤器@WebFilter的使用以及简单介绍限流算法

    过滤器(Filter)实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理,通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理,大致流程如下图 一般情况下,我们针对一些敏感的参数,例如密码、身份证号等,给它加密,防止报文明文

    2024年02月03日
    浏览(28)
  • CAN通信配置过滤器和使用三个邮箱发送

    RM比赛用的电机基本都使用CAN通信,但是一条CAN线上只用一个发送邮箱在挂在设备多的情况可能会导致发送不完,但其实完全可以把三个发送邮箱都用上。这里贴一下自己的CAN筛选器,接收以及发送的代码。 完整的工程可以看我开源的飞机云台程序~ 项目代码开源地址: https

    2024年02月06日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包