• 一、Spring Boot集成Spring Security之自动装配


    二、实现功能及软件版本说明

    1. 使用Spring Boot集成Spring Security实现Servlet项目的安全个性化配置
    2. Spring Boot版本:2.7.18
    3. Spring Security版本:5.7.11

    三、创建Spring Boot项目

    1. 创建Spring Boot项目,目录结构如下

      8c8ee28f2cdf81127643a3901b4833f

    2. 引入Spring Security包,完整pom.xml如下

    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
    
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.7.18version>
            <relativePath/>
        parent>
    
        <groupId>com.yugroupId>
        <artifactId>spring-boot-security2-demoartifactId>
        <version>0.0.1-SNAPSHOTversion>
        <name>spring-boot-security2-demoname>
        <description>Spring Boot集成Spring Security样例description>
    
        <properties>
            <java.version>8java.version>
        properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-securityartifactId>
            dependency>
        dependencies>
    
    project>
    
    

    四、查看自动装配配置类

    • 查看Security Servlet相关自动装配配置类

      1728632355558

    五、自动装配配置类之SecurityAutoConfiguration

    1、部分源码

    @AutoConfiguration
    @ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
    @EnableConfigurationProperties(SecurityProperties.class)
    @Import({ SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class })
    public class SecurityAutoConfiguration {
    
    	@Bean
    	@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
    	public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
    		return new DefaultAuthenticationEventPublisher(publisher);
    	}
    
    }
    
    

    2、主要作用

    1. 导入SpringBootWebSecurityConfiguration

    3、SpringBootWebSecurityConfiguration

    1)、部分源码

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    class SpringBootWebSecurityConfiguration {
    
    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnDefaultWebSecurity
    	static class SecurityFilterChainConfiguration {
    
    		@Bean
    		@Order(SecurityProperties.BASIC_AUTH_ORDER)
    		SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    			http.authorizeRequests().anyRequest().authenticated();
    			http.formLogin();
    			http.httpBasic();
    			return http.build();
    		}
    
    	}
    
    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
    	@ConditionalOnClass(EnableWebSecurity.class)
    	@EnableWebSecurity
    	static class WebSecurityEnablerConfiguration {
    
    	}
    
    }
    

    2)、主要作用

    1. 默认Security配置(Spring容器中没有SecurityFilterChain和WebSecurityConfigurerAdapter)时,向Spring容器中注入默认过滤器链,即用户没有自定义过滤器链时,生成默认过滤器链

      image-20241010174044684

    2. Spring容器中不存在名称为springSecurityFilterChain对象时,启用WebSecurity,即用户未显示的启用WebSecurity时,隐式的启用WebSecurity

      image-20241010174140639

    4、@EnableWebSecurity

    1)、部分源码

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Documented
    @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
    		HttpSecurityConfiguration.class })
    @EnableGlobalAuthentication
    @Configuration
    public @interface EnableWebSecurity {
    
    	/**
    	 * Controls debugging support for Spring Security. Default is false.
    	 * @return if true, enables debug support with Spring Security
    	 */
    	boolean debug() default false;
    
    }
    

    2)、主要作用

    1. 导入WebSecurityConfiguration
    2. 导入HttpSecurityConfiguration

    5、WebSecurityConfiguration

    1)、部分源码

    @Configuration(proxyBeanMethods = false)
    public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    	public Filter springSecurityFilterChain() throws Exception {
    		boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
    		boolean hasFilterChain = !this.securityFilterChains.isEmpty();
    		Assert.state(!(hasConfigurers && hasFilterChain),
    				"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
    		if (!hasConfigurers && !hasFilterChain) {
    			WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
    					.postProcess(new WebSecurityConfigurerAdapter() {
    					});
    			this.webSecurity.apply(adapter);
    		}
    		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
    			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
    			for (Filter filter : securityFilterChain.getFilters()) {
    				if (filter instanceof FilterSecurityInterceptor) {
    					this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter);
    					break;
    				}
    			}
    		}
    		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
    			customizer.customize(this.webSecurity);
    		}
    		return this.webSecurity.build();
    	}
    }
    

    2)、主要作用

    1. 两种方式注册过滤器链:
      • 继承WebSecurityConfigurerAdapter(本质是实现SecurityConfigurer接口) (已弃用)
      • 直接向Spring容器中注册SecurityFilterChain对象
    2. 没有默认的过滤器链时,使用WebSecurityConfigurerAdapter中默认配置生成过滤器链
    3. 根据配置的SecurityFilterChain集合构建FilterChainProxy类型的对象并注入到Spring容器中名称为springSecurityFilterChain

    6、HttpSecurityConfiguration

    1)、部分源码

    @Configuration(proxyBeanMethods = false)
    class HttpSecurityConfiguration {
    
    	@Bean(HTTPSECURITY_BEAN_NAME)
    	@Scope("prototype")
    	HttpSecurity httpSecurity() throws Exception {
    		WebSecurityConfigurerAdapter.LazyPasswordEncoder passwordEncoder = new WebSecurityConfigurerAdapter.LazyPasswordEncoder(
    				this.context);
    		AuthenticationManagerBuilder authenticationBuilder = new WebSecurityConfigurerAdapter.DefaultPasswordEncoderAuthenticationManagerBuilder(
    				this.objectPostProcessor, passwordEncoder);
    		authenticationBuilder.parentAuthenticationManager(authenticationManager());
    		authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
    		HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
    		// @formatter:off
    		http
    			.csrf(withDefaults())
    			.addFilter(new WebAsyncManagerIntegrationFilter())
    			.exceptionHandling(withDefaults())
    			.headers(withDefaults())
    			.sessionManagement(withDefaults())
    			.securityContext(withDefaults())
    			.requestCache(withDefaults())
    			.anonymous(withDefaults())
    			.servletApi(withDefaults())
    			.apply(new DefaultLoginPageConfigurer<>());
    		http.logout(withDefaults());
    		// @formatter:on
    		applyDefaultConfigurers(http);
    		return http;
    	}
    }
    

    2)、主要作用

    1. Spring容器中注册HttpSecurity对象
    2. httpSecurity用于配置构建自定义过滤器链

    六、自动装配配置类之UserDetailsServiceAutoConfiguration

    1、部分源码

    @AutoConfiguration
    @ConditionalOnClass(AuthenticationManager.class)
    @ConditionalOnBean(ObjectPostProcessor.class)
    @ConditionalOnMissingBean(
    		value = { AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class,
    				AuthenticationManagerResolver.class },
    		type = { "org.springframework.security.oauth2.jwt.JwtDecoder",
    				"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector",
    				"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository",
    				"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository" })
    public class UserDetailsServiceAutoConfiguration {
    	@Bean
    	@Lazy
    	public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties,
    			ObjectProvider passwordEncoder) {
    		SecurityProperties.User user = properties.getUser();
    		List roles = user.getRoles();
    		return new InMemoryUserDetailsManager(User.withUsername(user.getName())
    			.password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
    			.roles(StringUtils.toStringArray(roles))
    			.build());
    	}
    	private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
    		String password = user.getPassword();
    		if (user.isPasswordGenerated()) {
    			logger.warn(String.format(
    					"%n%nUsing generated security password: %s%n%nThis generated password is for development use only. "
    							+ "Your security configuration must be updated before running your application in "
    							+ "production.%n",
    					user.getPassword()));
    		}
    		if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
    			return password;
    		}
    		return NOOP_PASSWORD_PREFIX + password;
    	}
    

    2、主要作用

    1. 用户未自定义认证接口时,生成默认认证接口inMemoryUserDetailsManager(基于内存用户认证)
    2. 生成默认名称为user,密码为随机生成的uuid(项目启动时会打印在控制台中),角色为空的用户存入内存中
    #UserDetailsServiceAutoConfiguration.inMemoryUserDetailsManager方法中获取user对象
    SecurityProperties.User user = properties.getUser();
    #SecurityProperties中的User类
    public static class User {
    		private String name = "user";
    		private String password = UUID.randomUUID().toString();
    		private List roles = new ArrayList<>();
    	}
    
    1. 通过配置文件可以修改默认用户名、密码、角色(示例如下)

      image-20241010174253004

    七、自动装配配置类之SecurityFilterAutoConfiguration

    1、部分源码

    @AutoConfiguration(after = SecurityAutoConfiguration.class)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @EnableConfigurationProperties(SecurityProperties.class)
    @ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
    public class SecurityFilterAutoConfiguration {
    
    	private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
    
    	@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;
    	}
    
    	private EnumSet getDispatcherTypes(SecurityProperties securityProperties) {
    		if (securityProperties.getFilter().getDispatcherTypes() == null) {
    			return null;
    		}
    		return securityProperties.getFilter()
    			.getDispatcherTypes()
    			.stream()
    			.map((type) -> DispatcherType.valueOf(type.name()))
    			.collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
    	}
    
    }
    
    

    2、主要作用

    1. 注册DelegatingFilterProxyRegistrationBean(委托过滤器代理注册Bean)
    2. 设置代理目标Bean对象名称为springSecurityFilterChain
  • 相关阅读:
    大聪明教你学Java | 比校验文件后缀名更靠谱的上传文件校验方式 —— 文件魔数校验
    MySQL_数据库的约束
    小谈设计模式(10)—原型模式
    行测-图形推理-1-汉字类
    使用OpenCV将图像转换为NV12格式并加载NV12数据
    C#word(2007)操作类
    严格模式——let和const——箭头函数——解构赋值——字符串模板symbol——Set和Map——生成器函数
    Nginx日志管理之访问日志配置
    golang保留小数点后两位,不四舍五入
    抖音API:item_search_video-根据关键词取视频列表
  • 原文地址:https://www.cnblogs.com/sanxiaolq/p/18458562