• SpringSecurity - 启动流程分析(九)- CorsFilter 过滤器



    活动地址:CSDN21天学习挑战赛

    前言

    在上一篇 SpringSecurity - 启动流程分析(八)- CsrfFilter 过滤器 文章中我们了解了 SpringSecurity - 启动流程分析(四)- 默认过滤器 中提到的 CsrfFilter 过滤器 来抵御 Csrf 攻击。跨域问题也是我们 Web 开发中经常遇到的问题,接下来在这篇文章中我们就来看一下 SpringSecurity 是如何使用 CorsFilter解决跨域问题

    概述

    对于什么是 跨域问题同源策略,这里就不再赘述了,直接从源码开始分析

    分析

    HttpSecurity 加载 cors() 配置

    /**
     * Adds a {@link CorsFilter} to be used. If a bean by the name of corsFilter is
     * provided, that {@link CorsFilter} is used. Else if corsConfigurationSource is
     * defined, then that {@link CorsConfiguration} is used. Otherwise, if Spring MVC is
     * on the classpath a {@link HandlerMappingIntrospector} is used.
     * @return the {@link CorsConfigurer} for customizations
     * @throws Exception
     */
    public CorsConfigurer<HttpSecurity> cors() throws Exception {
    	return getOrApply(new CorsConfigurer<>());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    从源码注释中可以看到,要使用跨域配置有三种方式:

    1. 配置 CorsFilterIoC 容器
    2. 配置 CorsConfiguration
    3. 在类路径上存在 SpringMVC 提供的 HandlerMappingIntrospector

    接下来看一下 CorsConfigurer 的具体源码:

    public class CorsConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CorsConfigurer<H>, H> {
    
    	private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector";
    
    	private static final String CORS_CONFIGURATION_SOURCE_BEAN_NAME = "corsConfigurationSource";
    
    	// 从容器中获取名称为 corsFilter 的 Bean
    	private static final String CORS_FILTER_BEAN_NAME = "corsFilter";
    
    	private CorsConfigurationSource configurationSource;
    
    	/**
    	 * Creates a new instance
    	 *
    	 * @see HttpSecurity#cors()
    	 */
    	public CorsConfigurer() {
    	}
    
    	// 可以传入 CorsConfigurationSource
    	public CorsConfigurer<H> configurationSource(CorsConfigurationSource configurationSource) {
    		this.configurationSource = configurationSource;
    		return this;
    	}
    
    	@Override
    	public void configure(H http) {
    		ApplicationContext context = http.getSharedObject(ApplicationContext.class);
    		CorsFilter corsFilter = getCorsFilter(context);
    		Assert.state(corsFilter != null, () -> "Please configure either a " + CORS_FILTER_BEAN_NAME + " bean or a "
    				+ CORS_CONFIGURATION_SOURCE_BEAN_NAME + "bean.");
    		http.addFilter(corsFilter);
    	}
    
    	// 返回 CorsFilter,是从容器中获取还是创建一个
    	private CorsFilter getCorsFilter(ApplicationContext context) {
    		// 构造器中是否有传入 configurationSource 
    		if (this.configurationSource != null) {
    			return new CorsFilter(this.configurationSource);
    		}
    		// IoC 容器中是否有名为 corsFilter 的 Bean
    		boolean containsCorsFilter = context.containsBeanDefinition(CORS_FILTER_BEAN_NAME);
    		if (containsCorsFilter) {
    			return context.getBean(CORS_FILTER_BEAN_NAME, CorsFilter.class);
    		}
    		boolean containsCorsSource = context.containsBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME);
    		if (containsCorsSource) {
    			CorsConfigurationSource configurationSource = context.getBean(CORS_CONFIGURATION_SOURCE_BEAN_NAME,
    					CorsConfigurationSource.class);
    			return new CorsFilter(configurationSource);
    		}
    		boolean mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, context.getClassLoader());
    		if (mvcPresent) {
    			return MvcCorsFilter.getMvcCorsFilter(context);
    		}
    		return null;
    	}
    
    	static class MvcCorsFilter {
    
    		private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";
    
    		/**
    		 * This needs to be isolated into a separate class as Spring MVC is an optional
    		 * dependency and will potentially cause ClassLoading issues
    		 * @param context
    		 * @return
    		 */
    		private static CorsFilter getMvcCorsFilter(ApplicationContext context) {
    			if (!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
    				throw new NoSuchBeanDefinitionException(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, "A Bean named "
    						+ HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + " of type "
    						+ HandlerMappingIntrospector.class.getName()
    						+ " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext.");
    			}
    			HandlerMappingIntrospector mappingIntrospector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME,
    					HandlerMappingIntrospector.class);
    			return new CorsFilter(mappingIntrospector);
    		}
    
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    CorsConfigurer 的主要作用是为了构建 CorsFilter 并加入到 SecurityFilterChain 中,CorsFilter 是执行具体的过滤规则:

    public class CorsFilter extends OncePerRequestFilter {
        private final CorsConfigurationSource configSource;
        private CorsProcessor processor = new DefaultCorsProcessor();
    
    	// 在定义 CorsFilter 的同时,需要传入 CorsConfigurationSource
        public CorsFilter(CorsConfigurationSource configSource) {
            Assert.notNull(configSource, "CorsConfigurationSource must not be null");
            this.configSource = configSource;
        }
    
        public void setCorsProcessor(CorsProcessor processor) {
            Assert.notNull(processor, "CorsProcessor must not be null");
            this.processor = processor;
        }
    
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
            // 这里是判断请求是否符合同源策略,可以查看上面默认的 DefaultCorsProcessor
            boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
            if (isValid && !CorsUtils.isPreFlightRequest(request)) {
                filterChain.doFilter(request, response);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    举例

    这里给出一个示例,往容器中添加一个名为 corsFilterBean,在执行请求的时候就会判断请求头中的属性是否符合配置的同源设置

    @Configuration
    public class CorsConfig {
    
        private CorsConfiguration buildConfig() {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
    
    		// 接收所有来源的请求
            // corsConfiguration.addAllowedOriginPattern("*");
            // 接收指定来源的请求
            corsConfiguration.addAllowedOriginPattern("http://xxx.com");
            corsConfiguration.addAllowedOriginPattern("http://aa.xxx.com");
    
            corsConfiguration.addAllowedHeader("*");
            corsConfiguration.addAllowedMethod("*");
            corsConfiguration.setAllowCredentials(true);
            return corsConfiguration;
        }
    
        @Bean
        public CorsFilter corsFilter() {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", buildConfig());
            return new CorsFilter(source);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    用户眼中的 tensor、TensorFlow 系统中的 tensor、tensor 高阶用法 DLPack
    2022年湖北大学招生简章--成人高等教育高起专、专升本学历提升
    .NET 缓存:内存缓存 IMemoryCache、分布式缓存 IDistributedCache(Redis)
    MySQL 基础知识(九)之视图
    零基础认识大语言模型工作原理
    STL初始、容器遍历方式及嵌套
    数据库系统原理与应用教程(058)—— MySQL 练习题(二):单选题
    windows删除大量文件的优秀方式
    Linux系统彻底卸载MySQL数据库
    c++ 运算符重载(二)
  • 原文地址:https://blog.csdn.net/qiaohao0206/article/details/126452717