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

cnblogs 2024-10-11 16:09:00 阅读 59

一、Spring Boot集成Spring Security专栏

一、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如下

<code><?xml version="1.0" encoding="UTF-8"?>code>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"code>

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">code>

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.7.18</version>

<relativePath/>

</parent>

<groupId>com.yu</groupId>

<artifactId>spring-boot-security2-demo</artifactId>

<version>0.0.1-SNAPSHOT</version>

<name>spring-boot-security2-demo</name>

<description>Spring Boot集成Spring Security样例</description>

<properties>

<java.version>8</java.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-security</artifactId>

</dependency>

</dependencies>

</project>

四、查看自动装配配置类

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

    1728632355558

五、自动装配配置类之SecurityAutoConfiguration

1、部分源码

<code>@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)、部分源码

<code>@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> passwordEncoder) {

SecurityProperties.User user = properties.getUser();

List<String> 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<String> roles = new ArrayList<>();

}

  1. 通过配置文件可以修改默认用户名、密码、角色(示例如下)

image-20241010174253004

七、自动装配配置类之SecurityFilterAutoConfiguration

1、部分源码

<code>@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<DispatcherType> 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


声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。