一、自动配置
在 Spring Security 6.x 系列(1)—— 初识Spring Security 中我们只引入spring-boot-starter-security
依赖,就可以实现登录认证,这些都得益于Spring Boot
的自动配置。
在spring-boot-autoconfigure
模块中集成了对Spring Security
的自动配置:
默认的配置是由 SecurityAutoConfiguration
、 UserDetailsServiceAutoConfiguration
、SecurityFilterAutoConfiguration
这三个自动配置类实现的。
二、SecurityAutoConfiguration
SecurityAutoConfiguration
主要是导入SpringBootWebSecurityConfiguration
配置类:
@AutoConfiguration(
before = {UserDetailsServiceAutoConfiguration.class}
)
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
@Import({SpringBootWebSecurityConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
public SecurityAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean({AuthenticationEventPublisher.class})
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
2.1 SpringBootWebSecurityConfiguration
在SpringBootWebSecurityConfiguration
配置类中,默认添加@EnableWebSecurity
注解,启用Spring Security
应用安全配置,完成SecurityFilterChain
对象实例化,并设置Http
相关规则:
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
class SpringBootWebSecurityConfiguration {
SpringBootWebSecurityConfiguration() {
}
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnMissingBean(
name = {"springSecurityFilterChain"}
)
@ConditionalOnClass({EnableWebSecurity.class})
@EnableWebSecurity
static class WebSecurityEnablerConfiguration {
WebSecurityEnablerConfiguration() {
}
}
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnDefaultWebSecurity
static class SecurityFilterChainConfiguration {
SecurityFilterChainConfiguration() {
}
@Bean
@Order(2147483642)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> {
// 配置所有的Http请求必须认证
((AuthorizeHttpRequestsConfigurer.AuthorizedUrl)requests.anyRequest()).authenticated();
});
// 开启表单登录认证
http.formLogin(Customizer.withDefaults());
// 开启basic认证
http.httpBasic(Customizer.withDefaults());
return (SecurityFilterChain)http.build();
}
}
}
2.1.1 DefaultSecurityFilterChain 源码分析
SecurityFilterChain
是一个接口,源码如下:
public interface SecurityFilterChain {
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}
而DefaultSecurityFilterChain
是Spring Security
中对其的默认实现类,源码也比较简单:
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
private final RequestMatcher requestMatcher;
private final List<Filter> filters;
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
this(requestMatcher, Arrays.asList(filters));
}
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
if (filters.isEmpty()) {
logger.info(LogMessage.format("Will not secure %s", requestMatcher));
}
else {
logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
}
this.requestMatcher = requestMatcher;
this.filters = new ArrayList<>(filters);
}
public RequestMatcher getRequestMatcher() {
return this.requestMatcher;
}
@Override
public List<Filter> getFilters() {
return this.filters;
}
@Override
public boolean matches(HttpServletRequest request) {
return this.requestMatcher.matches(request);
}
@Override
public String toString() {
return this.getClass().getSimpleName() + " [RequestMatcher=" + this.requestMatcher + ", Filters=" + this.filters
+ "]";
}
}
2.1.2 DefaultSecurityFilterChain 实例化分析
在上文源码中SecurityFilterChain
是通过(SecurityFilterChain)http.build()
完成实例化,跟踪进入HttpSecurity#performBuild
源码:
从上面看得出HttpSecurity
一个构建类,它的使命就是构建DefaultSecurityFilterChain
对象,debug
查看DefaultSecurityFilterChain
默认匹配所有请求,并默认存在15个过滤器:
当前还有一个疑问,就是HttpSecurity
是何时进行对象实例化的?请见下文。
2.2 @EnableWebSecurity
@EnableWebSecurity
中会导入多个配置类:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class})
@EnableGlobalAuthentication
public @interface EnableWebSecurity {
boolean debug() default false;
}
下文重点介绍下WebSecurityConfiguration
和HttpSecurityConfiguration
配置类的作用。
2.2.1 WebSecurityConfiguration
2.2.1.1 FilterChainProxy 实例化分析
在WebSecurityConfiguration
中会通过this.webSecurity.build()
方法完成Bean
名为springSecurityFilterChain
的FilterChainProxy
对象实例化:
跟踪进入WebSecurity#performBuild
源码:
@Override
protected Filter performBuild() throws Exception {
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this is done by exposing a SecurityFilterChain bean. "
+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
if (this.privilegeEvaluator == null) {
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
requestMatcherPrivilegeEvaluatorsEntries);
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
if (this.requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
}
else if (!this.observationRegistry.isNoop()) {
CompositeRequestRejectedHandler requestRejectedHandler = new CompositeRequestRejectedHandler(
new ObservationMarkingRequestRejectedHandler(this.observationRegistry),
new HttpStatusRequestRejectedHandler());
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
}
filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (this.debugEnabled) {
this.logger.warn("nn" + "********************************************************************n"
+ "********** Security debugging is enabled. *************n"
+ "********** This may include sensitive information. *************n"
+ "********** Do not use in a production system! *************n"
+ "********************************************************************nn");
result = new DebugFilter(filterChainProxy);
}
this.postBuildAction.run();
return result;
}
发现WebSecurity
同样是一个构建类,它的使命就是构建FilterChainProxy
对象。FilterChainProxy
是一个代理类,继承GenericFilterBean
(即是Servlet Filter
又是Spring Bean
),它管理了所有注入Spring IoC
容器的SecurityFilterChain
(DefaultSecurityFilterChain
是SecurityFilterChain
的默认实现),而在SecurityFilterChain
中又包含多个Spring Security
声明的Filter
。
2.2.2 HttpSecurityConfiguration
在HttpSecurityConfiguration#httpSecurity
中默认配置了很多Security Filters
并完成HttpSecurity
对象实例化(多实例)。
此处对上文提出的HttpSecurity
是何时进行对象实例化 的疑问完成了解答,也清晰了在2.1.2章节中为什么DefaultSecurityFilterChain
对象默认存在15个过滤器的原因。
三、UserDetailsServiceAutoConfiguration
UserDetailsServiceAutoConfiguration
只是通过加载Yml
配置文件生成一个默认用户,以便于开发测试:
@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 {
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);
public UserDetailsServiceAutoConfiguration() {
}
@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {
SecurityProperties.User user = properties.getUser();
List<String> roles = user.getRoles();
return new InMemoryUserDetailsManager(new UserDetails[]{User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)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()));
}
return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;
}
}
看到这里我们就能明白解 Spring Security 6.1.x 系列(1)—— 初识Spring Security 中通过在配置文件中定义用户名和密码能生效的原因。
四、SecurityFilterAutoConfiguration
在上文中,Bean
名为springSecurityFilterChain
的对象是FilterChainProxy
类型,在WebSecurityConfiguration
被实例化过,代理了Spring Security
中所有的SecurityFilterChain
。
因为Servlet
容器和Spring IoC
容器之间的Filter
生命周期并不匹配。
在SecurityFilterAutoConfiguration
自动配置类中,为了让Spring IoC
容器管理Filter
的生命周期,FilterChainProxy
(也就是springSecurityFilterChain
)便交由Spring Web
下的DelegatingFilterProxy
来代理。
而且FilterChainProxy
不会在任何过滤器Bean
上调用标准Servlet
过滤器生命周期方法,FilterChainProxy
的生命周期方法会委托给DelegatingFilterProxy
来执行。
DelegatingFilterProxy
是作为Servlet
容器和Spring IoC
容器的生命周期之间桥接的存在。
@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 = "springSecurityFilterChain";
public SecurityFilterAutoConfiguration() {
}
@Bean
@ConditionalOnBean(
name = {"springSecurityFilterChain"}
)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean("springSecurityFilterChain", new ServletRegistrationBean[0]);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(this.getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
return securityProperties.getFilter().getDispatcherTypes() == null ? null : (EnumSet)securityProperties.getFilter().getDispatcherTypes().stream().map((type) -> {
return DispatcherType.valueOf(type.name());
}).collect(Collectors.toCollection(() -> {
return EnumSet.noneOf(DispatcherType.class);
}));
}
}
五、总结
结合本篇源码分析对前文
Spring Security 6.x 系列(2)—— 基于过滤器的基础原理(一) 中提到的DelegatingFilterProxy
、FilterChainProxy
和Spring Security 6.x 系列(3)—— 基于过滤器的基础原理(二)中提到的SecurityFilterChain
和Security Filters
等概念会有更深刻的理解。
原文地址:https://blog.csdn.net/ctwy291314/article/details/134155035
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_10081.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!