SpringSecurity加载流程
2019-09-18 本文已影响0人
TZX_0710
上一篇文章我们采用了security实现了helloworld程序。并且成功看到了security的默认的登录页面。以及拦截功能
springboot2.1.8
启动配置详解
使用idea打开搜索选项卡
shift shift 输入spring.factories
会出现多个文件 找到 autoconfig结尾的文件

搜索security

这里是有9个相关security的类,暂时排除oauth2和reactive(springboot2之后的新特性)
剩下的类就3个
SecurityAutoConfiguration
UserDetailsServiceAutoConfiguration
SecurityFilterAutoConfiguration
SecurityAutoConfiguration源码
对于查看源码 idea打开可能会提示有个downonload source 对于喜欢阅读源码的小伙伴可以下载一下

@Configuration
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class) //该注解的参数对应的类必须存在否则不解析该类
@EnableConfigurationProperties(SecurityProperties.class)//让使用ConfigurationProperties注解的类生效
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
@import可以将一个普通的类导入到ioc容器当中
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
从上面的源码我们可以发现 该类首先初始化DefaultAuthenticationEventPublisher。
该类的主要作用一些对一些已知的认证异常通知包括设置一些自定义的认证异常在此不做详细描述
导入配置类SecurityProperties
该类主要是对security的一些配置
@ConfigurationProperties(prefix = "spring.security")
public class SecurityProperties {
public static class User {
/**
*默认security的用户
*/
private String name = "user";
/**
*默认密码 如果未配置用户密码 那么会默认生成一个UUID的密码
*/
private String password = UUID.randomUUID().toString();
/**
* 为默认用户授予角色
*/
private List<String> roles = new ArrayList<>();
private boolean passwordGenerated = true;
}
}
最后一行注解引入了其他的3个类
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
SpringBootWebSecurityConfiguration
对web的一个安全设置 如果用户自定义了web配置
同时检查了当前的servlet容器当中是否存在
WebSecurityConfigurerAdapter这个Bean 如果没有的话就帮助我们创建一个
所以我们如果需要实现自定义配置需要继承这个类 去实现自定义
SecurityDataConfiguration只有在导入Spring-data的引用才会生效
WebSecurityEnableConfiguration类 该类中加入了EnableWebSecurity
其主要作用是帮我们开启Security的httpsecurity的配置可以让用户进行自定义web的相关配置 比如路径拦截等
### UserDetailsServiceAutoConfiguration
根据用户的userDetail 把他们读取到内存当中 通过内存去读取判断权限
如果想要拓展 那么也需要实现该类
@Configuration
@ConditionalOnClass(AuthenticationManager.class)
@ConditionalOnBean(ObjectPostProcessor.class)
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class, UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {
private static final String NOOP_PASSWORD_PREFIX = "{noop}";
private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern.compile("^\\{.+}.*$");
private static final Log logger = LogFactory.getLog(UserDetailsServiceAutoConfiguration.class);
@Bean
@ConditionalOnMissingBean(
type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
@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.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
}
if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
return password;
}
return NOOP_PASSWORD_PREFIX + password;
}
}
SecurityFilterAutoConfiguration
/*
* {@link EnableAutoConfiguration Auto-configuration} for Spring Security's Filter.
* Configured separately from {@link SpringBootWebSecurityConfiguration} to ensure that
* the filter's order is still configured when a user-provided
* {@link WebSecurityConfiguration} exists
EnableAutoConfiguration 和SpringBootWebSecurityConfiguration分开配置 为用户提供websecurity服务
*/
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.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.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}
}
首先,我们发现这个类有一个@AutoConfigureAfter(SecurityAutoConfiguration.class),也就是说这个类要在我们讲的第一个SecurityAutoConfiguration才行,
然后它拿到我们一开始说的SecurityProperties,帮我们做了一个Filter:但是!这个filter具体是啥,它沒有直接告訴我們,只把它在spring中的bean的名字给出来了,springSecurityFilterChain,
也就是説存在一个这样名字的springsecurity的filter,然后被spring代理了,管理它的生命周期。但是从名字我们大概可以猜出,不只是一个filter,是一个filter列表,既然这样,那我们直接在项目中搜索,看那个地方有这个名字的bean
发现该类是在WebSecurityConfiguration中初始化的,那WebSecurityConfiguration又是在哪来的呢,上面我们说到@EnableWebSecurity的时候,开启WebSecurityAdapter的配置,其实那个时候已经导入了