文章目录
- *过滤器链分析*
- *4.1初始化流程分析*
- *4.1.1`ObjectPostProcessor`*
- *4.1.2`SecurityFilterChain`*
- *4.1.3`SecurityBuilder`*
- *`SecurityBuilder`*
- *`HttpSecurityBuilder`*
- *`AbstractSecurityBuilder`*
- *`AbstractConfiguredSecurityBuilder`*
- *`ProviderManagerBuilder`*
- *`AuthenticationManagerBuilder`*
- *`HttpSecurity`*
- *`WebSecurity`*
- *4.1.4`FilterChainProxy`*
- *4.1.5`SecurityConfigurer`*
- *`SecurityConfigurer`*
- *`SecurityConfigurerAdapter`*
- *`UserDetailsAwareConfigurer`*
- *`AbstractHttpConfigurer`*
- *`GlobalAuthenticationConfigurerAdapter`*
- *`WebSecurityConfigurer`*
- *`WebSecurityConfigurerAdapter`*
- *4.1.6初始化流程分析*
- *`WebSecurityConfiguration`*
- *`AuthenticationConfiguration`*
- *4.2`ObjectPostProcessor`使用*
- *4.3多种用户定义方式*
- *4.4定义多个过滤器链*
- *4.5静态资源过滤*
- *4.6使用JSON格式登录*
- *4.7添加登录验证码*
过滤器链分析
Spring security中,所有功能都是通过过滤器来实现的,这些过滤器组成一个完整的过滤器链。
4.1初始化流程分析
为了更好的理解,需要先大致介绍下spring security中一些常见的关键组件。
4.1.1ObjectPostProcessor
ObjectPostProcessor
是spring security中使用频率最高的组件之一,它是一个对象后置处理器,也就是当一个对象创建成功后,如果还有一些额外的事情需要补充,那么可以通过它来进行处理。
/**
* 允许初始化对象。通常,这用于调用Aware中的方法,InitializingBean.afterPropertiesSet()以及
* 确保DisposableBean.destroy()被调用。
* Type parameters: <T> – 此ObjectPostProcessor支持的对象类型的界限
*/
public interface ObjectPostProcessor<T> {
// 初始化对象,可能返回一个应该使用的修改过的实例。完成对象的二次处理
<O extends T> O postProcess(O object);
}
AutowireBeanFactoryObjectPostProcessor
:由于spring security中大量采用了java配置,许多过滤器都是直接new
出来的,这些直接new
出来的对象并不会自动注入到spring容器中。Spring security这样做的本意是为了简化配置,但是却带来了另外一个问题就是,大量new
出来的对象需要手动注册到spring容器中去。AutowireBeanFactoryObjectPostProcessor
对象所承担的就是这件事,一个对象new
出来之后,只要调用AutowireBeanFactoryObjectPostProcessor#postProcess
方法,就可以成功注入到spring容器中,它的实现原理就是通过调用spring容器中的AutowireCapableBeanFactory
对象将一个new
出来的对象注入到spring容器中去。CompositeObjectPostProcessor
:一个对象可以有一个后置处理器,也可以自定义多个对象后置处理器。CompositeObjectPostProcessor
是一个组合的对象后置处理器,它里边维护了一个List
集合,集合中存放了某一个对象的所有后置处理器,当需要执行对象的后置处理器时,会遍历集合中的所有ObjectPostProcessor
实例,分别调用实例的postProcess
方法进行对象的后置处理。在spring security中,最终使用的对象后置处理器其实就是CompositeObjectPostProcessor
,它里边的集合默认只有一个对象,就是AutowireBeanFactoryObjectPostProcessor
。在spring security中,开发者可以灵活地配置项目中需要哪些spring security的过滤器,一旦选定过滤器之后,每一个过滤器都会有一个对应的配置器,叫做
xxxConfigurer
(例如CorsConfigurer
、CsrfConfigurer
等),过滤器都是在相应的配置器中new
出来的,然后在postProcess
方法中处理一遍,就将这些过滤器注入到spring容器中了。
4.1.2SecurityFilterChain
SecurityFilterChain
就是spring security中的过滤器链对象:
/**
* 定义一个能够与HttpServletRequest匹配的过滤器链,以决定是否适用于该请求。
* 用于配置FilterChainProxy。
*/
public interface SecurityFilterChain {
// 判断request请求是否应该被当前过滤器链所处理
boolean matches(HttpServletRequest request);
// 返回一个spring security中的过滤器集合。如果matches返回true,那么request
// 请求就会在getFilters方法中返回的集合中被处理
List<Filter> getFilters();
}
SecurityFilterChain
只有一个默认的实现类就是DefaultSecurityFilterChain
:
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
private final RequestMatcher requestMatcher;
private final List<Filter> filters;
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
this(requestMatcher, Arrays.asList(filters));
}
// 需要传入两个对象,一个是请求匹配器requestMatcher,另一个则是过滤器集合或者过滤器数组filters
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> 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);
}
}
4.1.3SecurityBuilder
Spring security中所有需要构建的对象都可以通过
SecurityBuilder
来实现,默认的过滤器链、代理过滤器、AuthenticationManager
等,都可以通过SecurityBuilder
来构建。
SecurityBuilder
/**
* 用来创建一个对象的接口。不同的SecurityBuilder将来会构建出不同的对象。
* Type parameters: <O> – 具体构建的对象
*/
public interface SecurityBuilder<O> {
O build() throws Exception;
}
HttpSecurityBuilder
HttpSecurityBuilder
是用来构建HttpSecurity
对象的:
/**
* 默认情况下HttpSecurityBuilder的实现类只有一个HttpSecurity,所以可以将H都当成
* HttpSecurity来理解。
* HttpSecurityBuilder继承自SecurityBuilder接口,同时也指定了SecurityBuilder
* 中的泛型为DefaultSecurityFilterChain,也就是说,HttpSecurityBuilder最终想要
* 构建的对象是DefaultSecurityFilterChain
*/
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
extends SecurityBuilder<DefaultSecurityFilterChain> {
// 用来获取一个xxxConfigurer这样的配置器
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(Class<C> clazz);
// 用来移除一个配置器(相当于从spring security过滤器链中移除)
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(Class<C> clazz);
// 设置一个可以在多个配置器之间共享的对象
<C> void setSharedObject(Class<C> sharedType, C object);
// 获取一个可以在多个配置器之间共享的对象
<C> C getSharedObject(Class<C> sharedType);
// 配置一个认证器
H authenticationProvider(AuthenticationProvider authenticationProvider);
// 配置一个数据源
H userDetailsService(UserDetailsService userDetailsService) throws Exception;
// 在某一个过滤器之后添加一个自定义的过滤器
H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);
// 在某一个过滤器之前添加一个自定义的过滤器
H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);
// 添加一个过滤器,必须是spring security提供的过滤器的一个实例或者其扩展,
// 添加完成之后,会自动进行过滤器的排序
H addFilter(Filter filter);
}
AbstractSecurityBuilder
AbstractSecurityBuilder
实现了SecurityBuilder
接口,并对build
做了完善,确保只build
一次:
// Type parameters: <O> – 正在构建的Object的类型
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
// 确保即使在多线程环境下,配置类也只能构建一次
private AtomicBoolean building = new AtomicBoolean();
private O object;
// 设置为final,子类不能重写
@Override
public final O build() throws Exception {
// 通过building变量来控制配置类只构建一次
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
// 返回构建的对象
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
// 执行具体的构建工作
protected abstract O doBuild() throws Exception;
}
AbstractConfiguredSecurityBuilder
源码较长,需要分开来看。首先在
AbstractConfiguredSecurityBuilder
中声明了一个枚举类,用来描述构建过程的不同状态:
private enum BuildState {
// 配置类构建前
UNBUILT(0),
// 初始化中(初始化完成之前是这个状态)
INITIALIZING(1),
// 配置中(开始构建之前是这个状态)
CONFIGURING(2),
// 构建中
BUILDING(3),
// 构建完成
BUILT(4);
private final int order;
BuildState(int order) {
this.order = order;
}
// 判断是否正在初始化中
public boolean isInitializing() {
return INITIALIZING.order == this.order;
}
// 判断是否已完成配置
public boolean isConfigured() {
return this.order >= CONFIGURING.order;
}
}
AbstractConfiguredSecurityBuilder
类的相关方法如下:
/**
* Type parameters: <O> – 构建器返回的对象
* Type parameters: <O> – 构建器的类型(由基类返回)
*/
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
// 用来存放所有的配置类
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
configurer.addObjectPostProcessor(this.objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
}
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
// 用来将所有的配置类保存到configurers中
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (this.configurers) {
if (this.buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
}
List<SecurityConfigurer<O, B>> configs = null;
// 判断是否允许相同类型的配置类存在,默认情况下,普通配置类为false,所以集合中的始终只有一个配置类,
// 如果在AuthenticationManagerBuilder中设置allowConfigurersOfSameType为true,此时相同
// 类型的配置类可以有多个
if (this.allowConfigurersOfSameType) {
configs = this.configurers.get(clazz);
}
configs = (configs != null) ? configs : new ArrayList<>(1);
configs.add(configurer);
this.configurers.put(clazz, configs);
if (this.buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
// 从configurers中返回某一个配置类对应的所有实例
public <C extends SecurityConfigurer<O, B>> List<C> getConfigurers(Class<C> clazz) {
List<C> configs = (List<C>) this.configurers.get(clazz);
if (configs == null) {
return new ArrayList<>();
}
return new ArrayList<>(configs);
}
// 从configurers中移除某一个配置类对应的所有实例并返回
public <C extends SecurityConfigurer<O, B>> List<C> removeConfigurers(Class<C> clazz) {
List<C> configs = (List<C>) this.configurers.remove(clazz);
if (configs == null) {
return new ArrayList<>();
}
return new ArrayList<>(configs);
}
// 获取配置类实例,但是只获取集合中第一项
public <C extends SecurityConfigurer<O, B>> C getConfigurer(Class<C> clazz) {
List<SecurityConfigurer<O, B>> configs = this.configurers.get(clazz);
if (configs == null) {
return null;
}
return (C) configs.get(0);
}
// 移除配置类对应的所有实例,并返回实例中的第一项
public <C extends SecurityConfigurer<O, B>> C removeConfigurer(Class<C> clazz) {
List<SecurityConfigurer<O, B>> configs = this.configurers.remove(clazz);
if (configs == null) {
return null;
}
return (C) configs.get(0);
}
// 将所有的配置类实例放到一个集合中返回,在配置类初始化和配置的时候,会调用到该方法
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
List<SecurityConfigurer<O, B>> result = new ArrayList<>();
for (List<SecurityConfigurer<O, B>> configs : this.configurers.values()) {
result.addAll(configs);
}
return result;
}
}
其最核心的方法是
doBuild
:
// 一边更新构建状态,一边执行构建方法
@Override
protected final O doBuild() throws Exception {
synchronized (this.configurers) {
this.buildState = BuildState.INITIALIZING;
// 空的初始化方法,如果需要在初始化之前做一些准备工作,可以自己重写该方法
beforeInit();
// 遍历所有的配置类,并调用其方法完成初始化操作
init();
this.buildState = BuildState.CONFIGURING;
// 默认空方法
beforeConfigure();
// 遍历所有的配置类,分别调用其configure方法完成配置
configure();
this.buildState = BuildState.BUILDING;
// 用来做最终的构建操作,抽象方法,具体的实现则在不同的配置类中
O result = performBuild();
this.buildState = BuildState.BUILT;
return result;
}
}
ProviderManagerBuilder
// 指定构建的对象是AuthenticationManager
public interface ProviderManagerBuilder<B extends ProviderManagerBuilder<B>>
extends SecurityBuilder<AuthenticationManager> {
B authenticationProvider(AuthenticationProvider authenticationProvider);
}
AuthenticationManagerBuilder
用来构建
AuthenticationManager
(仅列出核心代码):
public class AuthenticationManagerBuilder
extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder>
implements ProviderManagerBuilder<AuthenticationManagerBuilder> {
// 第二个参数为true,表示允许相同类型的配置类同时存在,参考AbstractConfiguredSecurityBuilder
public AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
super(objectPostProcessor, true);
}
// 用来给一个AuthenticationManager设置parent
public AuthenticationManagerBuilder parentAuthenticationManager(AuthenticationManager authenticationManager) {
if (authenticationManager instanceof ProviderManager) {
eraseCredentials(((ProviderManager) authenticationManager).isEraseCredentialsAfterAuthentication());
}
this.parentAuthenticationManager = authenticationManager;
return this;
}
// 用来配置基于内存的数据源,该方法会自动创建InMemoryUserDetailsManagerConfigurer配置类,并最终
// 将该配置类添加到父类的configurers变量中。由于设置了允许相同类型的配置类同时存在,因此
// inMemoryAuthentication方法可以反复调用多次
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
throws Exception {
return apply(new InMemoryUserDetailsManagerConfigurer<>());
}
// 同上
public JdbcUserDetailsManagerConfigurer<AuthenticationManagerBuilder> jdbcAuthentication() throws Exception {
return apply(new JdbcUserDetailsManagerConfigurer<>());
}
// 同上
public <T extends UserDetailsService> DaoAuthenticationConfigurer<AuthenticationManagerBuilder, T> userDetailsService(
T userDetailsService) throws Exception {
this.defaultUserDetailsService = userDetailsService;
return apply(new DaoAuthenticationConfigurer<>(userDetailsService));
}
// 用来向authenticationProviders集合中添加AuthenticationProvider对象。
// 一个AuthenticationManager实例中包含多个AuthenticationProvider实例,
// 那么,多个AuthenticationProvider实例可以通过authenticationProvider方法进行添加
@Override
public AuthenticationManagerBuilder authenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProviders.add(authenticationProvider);
return this;
}
// 执行具体的构建工作,常用的AuthenticationManager实例就是ProviderManager,所以这里创建
// ProviderManager对象,并且配置authenticationProviders和parentAuthenticationManager对象,
// ProviderManager对象创建成功之后,再去对象后置处理器中处理一遍再返回
@Override
protected ProviderManager performBuild() throws Exception {
if (!isConfigured()) {
return null;
}
ProviderManager providerManager = new ProviderManager(this.authenticationProviders,
this.parentAuthenticationManager);
if (this.eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(this.eraseCredentials);
}
if (this.eventPublisher != null) {
providerManager.setAuthenticationEventPublisher(this.eventPublisher);
}
providerManager = postProcess(providerManager);
return providerManager;
}
}
HttpSecurity
HttpSecurity
的主要作用是用来构建一条过滤器链,并反映到代码上,也就是构建一个DefaultSecurityFilterChain
对象。
一个DefaultSecurityFilterChain
对象包含一个路径匹配器和多个spring security过滤器,HttpSecurity
中通过收集各种各样的xxxConfigurer
,将spring security过滤器对应的配置类收集起来,并保存到父类AbstractConfiguredSecurityBuilder
的configurers
变量中,在后续的构建过程中,再将这些xxxConfigurer
构建为具体的spring security过滤器,同时添加到HttpSecurity
的filters
对象中。
/**
* 其他过滤器的配置都和form表单登录配置类似,因此只讲一个。每一套过滤器链都会有一个AuthenticationManager对象来进行
* 认证操作(如果认证失败,则会调用AuthenticationManager的parent再次进行认证)
*/
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<DefaultSecurityFilterChain, HttpSecurity>
implements SecurityBuilder<DefaultSecurityFilterChain>, HttpSecurityBuilder<HttpSecurity> {
// 返回一个FormLoginConfigurer,开发者可以在该对象的基础上继续完善对form表单的配置
public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
return getOrApply(new FormLoginConfigurer<>());
}
// 开发者可以提前在外面配置好FormLoginConfigurer对象,然后直接传进来进行配置即可,
// 返回值HttpSecurity则可以在方法返回后直接进行其他过滤器的配置
public HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) throws Exception {
formLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));
return HttpSecurity.this;
}
// 配置用来执行认证的AuthenticationProvider对象
@Override
public HttpSecurity authenticationProvider(AuthenticationProvider authenticationProvider) {
getAuthenticationRegistry().authenticationProvider(authenticationProvider);
return this;
}
// 配置UserDetailsService对象
@Override
public HttpSecurity userDetailsService(UserDetailsService userDetailsService) throws Exception {
getAuthenticationRegistry().userDetailsService(userDetailsService);
return this;
}
private AuthenticationManagerBuilder getAuthenticationRegistry() {
return getSharedObject(AuthenticationManagerBuilder.class);
}
// 触发AuthenticationManager对象的构建
@Override
protected void beforeConfigure() throws Exception {
setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
}
// 进行DefaultSecurityFilterChain对象的构建,传入请求匹配器和过滤器集合filters,
// 在构建之前,会先按照既定的顺序对filters进行排序
@Override
protected DefaultSecurityFilterChain performBuild() {
this.filters.sort(OrderComparator.INSTANCE);
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
for (Filter filter : this.filters) {
sortedFilters.add(((OrderedFilter) filter).filter);
}
return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}
// 在某一个过滤器之后添加一个自定义的过滤器
@Override
public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
return addFilterAtOffsetOf(filter, 1, afterFilter);
}
// 在某一个过滤器之前添加一个自定义的过滤器
@Override
public HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) {
return addFilterAtOffsetOf(filter, -1, beforeFilter);
}
// 向过滤器链中添加一个过滤器,必须是spring security框架提供的过滤器的一个实例或者其扩展。实际上,
// 在每一个xxxConfigurer的configure方法中,都会调用addFilter方法将构建好的过滤器添加到
// HttpSecurity中的filters集合中
@Override
public HttpSecurity addFilter(Filter filter) {
Integer order = this.filterOrders.getOrder(filter.getClass());
if (order == null) {
throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
}
this.filters.add(new OrderedFilter(filter, order));
return this;
}
// 可以在指定位置添加一个过滤器。需要注意的是,在同一个位置添加多个过滤器并不会覆盖现有的过滤器
public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
return addFilterAtOffsetOf(filter, 0, atFilter);
}
// 调用父类的getConfigurer方法去查看是否已经有对应的配置类了,如果有,则直接返回;
// 如果没有,则调用apply方法添加到父类的configurers变量中
private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply(C configurer)
throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
return apply(configurer);
}
}
WebSecurity
相比于
HttpSecurity
,WebSecurity
是在一个更大的层面上去构建过滤器。
一个HttpSecurity
可以构建一个过滤器链,也就是一个DefaultSecurityFilterChain
,而一个项目可以存在多个HttpSecurity
对象,也就可以构建多个DefaultSecurityFilterChain
过滤器链。WebSecurity
负责将HttpSecurity
所构建的DefaultSecurityFilterChain
对象(可能有多个),以及其他一些需要忽略的请求,再次重新构建为一个FilterChainProxy
对象,同时添加上HTTP防火墙。
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>, ApplicationContextAware {
// 保存所有被忽略的请求,因为在实际项目中,例如一些静态资源是不需要经过spring security过滤器链的
private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
// 用来保存所有的HttpSecurity对象
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<>();
// 配置请求防火墙
public WebSecurity httpFirewall(HttpFirewall httpFirewall) {
this.httpFirewall = httpFirewall;
return this;
}
// 每一个HttpSecurity对象创建成功之后,通过该方法添加到securityFilterChainBuilders集合中
public WebSecurity addSecurityFilterChainBuilder(
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
// 具体的构建方法
@Override
protected Filter performBuild() throws Exception {
// 统计过滤器链的总个数(被忽略的请求个数+通过HttpSecurity创建出来的过滤器链的个数)
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
// 对于被忽略的请求,在构建对象时,只是传入了请求匹配器,而没有传入对应的过滤器链,
// 意味着这些被忽略掉的请求,将来不必经过spring security过滤器链
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
// 遍历集合,调用build方法构建DefaultSecurityFilterChain对象,并存入securityFilterChains
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
// 构建FilterChainProxy对象
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
// 设置防火墙
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
if (this.requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
this.postBuildAction.run();
// 返回构建好的FilterChainProxy对象,之后通过spring提供的DelegatingFilterProxy
// 将其嵌入到web filter(原生过滤器链中)
return result;
}
}
4.1.4FilterChainProxy
FilterChainProxy
通过DelegatingFilterProxy
代理过滤器被集成到web filter中,DelegatingFilterProxy
作为一个代理对象,它不承载具体的业务。所以,spring security中的过滤器的最终执行,就是在FilterChainProxy
中。FilterChainProxy
的源码比较长,拆分成一段一段来看:
// 保存的过滤器链
private List<SecurityFilterChain> filterChains;
// 过滤器链配置完成后的验证器,默认是NullFilterChainValidator,其实没有做任何验证
private FilterChainValidator filterChainValidator = new NullFilterChainValidator();
// 默认的防火墙对象
private HttpFirewall firewall = new StrictHttpFirewall();
public FilterChainProxy() {
}
public FilterChainProxy(SecurityFilterChain chain) {
this(Arrays.asList(chain));
}
public FilterChainProxy(List<SecurityFilterChain> filterChains) {
this.filterChains = filterChains;
}
由于
FilterChainProxy
本质上就是一个过滤器,因此它的核心方法就是doFilter
:
/**
* doFilter方法相当于是整个spring security过滤器链的入口,一些具体的过滤器例如SecurityContextPersistenceFilter,
* 都是在该方法之后执行的。作为整个过滤器链的入口,这里多了一个clearContext变量,如果是第一次执行该doFilter方法,
* 执行完成后,在finally代码块中需要从SecurityContextHolder里清除用户信息,这个主要是为了防止用户没有正确配置
* SecurityContextPersistenceFilter,从而导致登录用户信息没有被正确清除,进而发生内存泄露
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// doFilter第一次执行时,clearContext为true
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (!clearContext) {
doFilterInternal(request, response, chain);
return;
}
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
doFilterInternal(request, response, chain);
} catch (RequestRejectedException ex) {
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex);
} finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
在
doFilter
方法中,过滤器的具体执行则交给了doFilterInternal
方法:
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 将request对象转换为一个FirewalledRequest对象,这个转换过程会进行http防火墙处理
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
// 同时将response转换为HttpServletResponse
HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
// 获取当前请求对应的过滤器链
List<Filter> filters = getFilters(firewallRequest);
// 如果filters为null或者filters中没有元素,说明当前请求并不需要经过spring security过滤器链,
// 此时执行firewallRequest.reset对http防火墙中的属性进行重置,再执行chain.doFilter,
// 回到web filter中,spring security过滤器链将被跳过
if (filters == null || filters.size() == 0) {
firewallRequest.reset();
chain.doFilter(firewallRequest, firewallResponse);
return;
}
// 构建虚拟的过滤器链对象VirtualFilterChain(每次调用方法都会生成新的),并执行其doFilter
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
virtualFilterChain.doFilter(firewallRequest, firewallResponse);
}
// 遍历filterChains集合,进而判断出当前请求和哪一个过滤器链是对应的
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : this.filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
接下来看看
VirtualFilterChain
的具体实现:
private static final class VirtualFilterChain implements FilterChain {
// 表示原生的过滤器链,执行它的doFilter方法会回到web filter中
private final FilterChain originalChain;
// 存储的本次请求的Filter
private final List<Filter> additionalFilters;
// 当前请求对象
private final FirewalledRequest firewalledRequest;
// 过滤器链的大小
private final int size;
// 过滤器链执行的下标
private int currentPosition = 0;
// 给相应的变量赋值
private VirtualFilterChain(FirewalledRequest firewalledRequest, FilterChain chain,
List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
this.firewalledRequest = firewalledRequest;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
// 判断当前执行的下标是否等于过滤器链的大小,如果相等,则说明整个过滤器链中的所有过滤器已经挨个走一遍了,
// 此时先对http防火墙中的属性进行重置,然后调用originalChain.doFilter方法跳出spring security filter,
// 回到web filter;如果不相等,则currentPosition自增,然后从过滤器链集合中取出一个过滤器去执行,注意,
// 执行的时候第三个参数this表示当前对象(即VirtualFilterChain),这样在每一个过滤器执行完毕之后,
// 最后的chain.doFilter方法又会回到当前doFilter方法中,继续下一个过滤器的调用
if (this.currentPosition == this.size) {
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
this.originalChain.doFilter(request, response);
return;
}
this.currentPosition++;
Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
nextFilter.doFilter(request, response, this);
}
}
4.1.5SecurityConfigurer
SecurityConfigurer
的实现类比较多,主要梳理下常见的实现类:
SecurityConfigurer
// AbstractConfiguredSecurityBuilder里的init方法和configure
// 其实就是在遍历执行不同配置类的init和configure方法
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
// 用来完成配置类的初始化操作,例如SecurityBuilder的初始化
void init(B builder) throws Exception;
// 进行配置类的配置,例如SecurityBuilder的配置
void configure(B builder) throws Exception;
}
SecurityConfigurer
的子类非常多,因为每一个过滤器都有自己对应的xxxConfigurer
,着重介绍几个关键的:
SecurityConfigurerAdapter
public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>> implements SecurityConfigurer<O, B> {
// 为每一个配置类都提供一个SecurityBuilder对象,将来通过它构建出具体的配置对象
private B securityBuilder;
private CompositeObjectPostProcessor objectPostProcessor = new CompositeObjectPostProcessor();
@Override
public void init(B builder) throws Exception {
}
@Override
public void configure(B builder) throws Exception {
}
// 通过and方法返回SecurityBuilder对象,这样方便不同的配置类进行链式配置
public B and() {
return getBuilder();
}
protected final B getBuilder() {
Assert.state(this.securityBuilder != null, "securityBuilder cannot be null");
return this.securityBuilder;
}
protected <T> T postProcess(T object) {
return (T) this.objectPostProcessor.postProcess(object);
}
// 向复合的对象后置处理器添加新的ObjectPostProcessor实例
public void addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
}
public void setBuilder(B builder) {
this.securityBuilder = builder;
}
// 内部类,这是一个复合的对象后置处理器
private static final class CompositeObjectPostProcessor implements ObjectPostProcessor<Object> {
private List<ObjectPostProcessor<?>> postProcessors = new ArrayList<>();
public Object postProcess(Object object) {
for (ObjectPostProcessor opp : this.postProcessors) {
Class<?> oppClass = opp.getClass();
Class<?> oppType = GenericTypeResolver.resolveTypeArgument(oppClass, ObjectPostProcessor.class);
if (oppType == null || oppType.isAssignableFrom(object.getClass())) {
object = opp.postProcess(object);
}
}
return object;
}
private boolean addObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
boolean result = this.postProcessors.add(objectPostProcessor);
this.postProcessors.sort(AnnotationAwareOrderComparator.INSTANCE);
return result;
}
}
}
UserDetailsAwareConfigurer
UserDetailsAwareConfigurer
的子类主要负责配置用户认证相关的组件,如UserDetailsService
等,UserDetailsAwareConfigurer
中提供了获取UserDetailsService
的抽象方法,具体实现则在它的子类中。
AbstractDaoAuthenticationConfigurer
:完成对DaoAuthenticationProvider
的配置。UserDetailsServiceConfigurer
:完成对UserDetailsService
的配置。UserDetailsManagerConfigurer
:使用UserDetailsManager
构建用户对象,完成对AuthenticationManagerBuilder
的填充。JdbcUserDetailsManagerConfigurer
:配置JdbcUserDetailsManager
并填充到AuthenticationManagerBuilder
中。InMemoryUserDetailsManagerConfigurer
:配置InMemoryUserDetailsManager
。DaoAuthenticationConfigurer
:完成对DaoAuthenticationProvider
的配置。
AbstractHttpConfigurer
AbstractHttpConfigurer
主要是为了给在HttpSecurity
中使用的配置类添加一个方便的父类,提取出共同的操作。
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
// 表示禁用某一个配置,本质上就是从构建器的configurers集合中移除某一个配置类,
// 这样在将来构建的时候就不存在该配置类,那么对应的功能也就不存在
public B disable() {
getBuilder().removeConfigurer(getClass());
return getBuilder();
}
// 表示给某一个对象添加一个对象后置处理器,由于该方法的返回值是当前对象,所以该方法可以用在链式配置中
public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (T) this;
}
}
AbstractHttpConfigurer
的实现类比较多,基本上都用来配置各种各样的过滤器:
配置类名称 | 作用 |
| 配置基于http basic认证的过滤器 |
| 配置注销登录过滤器 |
| 配置请求缓存过滤器 |
| 配置remember-me登录过滤器 |
| 配置包装原始请求过滤器 |
| 配置提供默认登录页面的过滤器 |
| 配置session管理过滤器 |
| 配置一个共享的 |
| 配置异常处理过滤器 |
| 配置安全相关的响应头信息 |
| 配置防范CSRF攻击过滤器 |
| 配置OAuth2相关的过滤器 |
| 配置OAuth2认证请求重定向的过滤器 |
| 配置匿名过滤器 |
| 配置J2EE身份预校验过滤器 |
| 配置请求协议处理过滤器 |
| 配置处理跨域过滤器 |
| 配置登录信息存储和恢复的过滤器 |
| 配置OAuth2身份请求认证过滤器 |
| 身份认证配置类的父类 |
| 配置身份认证过滤器 |
| 配置OAuth2认证请求重定向的过滤器 |
| 配置OpenID身份认证过滤器 |
| 配置SAML2.0身份认证过滤器 |
| 配置X509身份认证过滤器 |
| 拦截器配置类的父类 |
| 配置基于URL的权限认证拦截器 |
| 配置基于SpEL表达式的URL权限认证拦截器 |
GlobalAuthenticationConfigurerAdapter
主要用于配置全局
AuthenticationManagerBuilder
,在AuthenticationConfiguration
类中会自动使用GlobalAuthenticationConfigurerAdapter
提供的bean来配置全局AuthenticationManagerBuilder
。
之前提到过,默认情况下,ProviderManager
有一个parent
,这个parent
就是通过这里的全局AuthenticationManagerBuilder
来构建的。GlobalAuthenticationConfigurerAdapter
有四个不同的子类:
InitializeAuthenticationProviderBeanManagerConfigurer
:初始化全局的AuthenticationProvider
对象。InitializeAuthenticationProviderManagerConfigurer
:配置全局的AuthenticationProvider
对象,配置过程就是从spring容器中查找AuthenticationProvider
并设置给全局的AuthenticationManagerBuilder
对象。InitializeUserDetailsBeanManagerConfigurer
:初始化全局的UserDetailsService
对象。InitializeUserDetailsManagerConfigurer
:配置全局的UserDetailsService
对象,配置过程就是从spring容器中查找UserDetailsService
,并设置给全局的AuthenticationManagerBuilder
对象。EnableGlobalAuthenticationAutowiredConfigurer
:从spring容器中加载被@EnableGlobalAuthentication
注解标记的bean。
WebSecurityConfigurer
这是一个空接口,可以通过它来自定义
WebSecurity
。其只有一个实现类就是WebSecurityConfigurerAdapter
,在大多数情况下,开发者通过继承WebSecurityConfigurerAdapter
来实现对WebSecurity
的自定义配置。
WebSecurityConfigurerAdapter
可以方便创建
WebSecurityConfigurer
实例的基类,开发者可以通过覆盖其中的方法完成对HttpSecurity
和WebSecurity
的定制。
在WebSecurityConfigurerAdapter
中声明了两个AuthenticationManagerBuilder
对象用来构建AuthenticationManager
:
// 负责构建局部的AuthenticationManager
private AuthenticationManagerBuilder authenticationBuilder;
// 负责构建全局的AuthenticationManager
private AuthenticationManagerBuilder localConfigureAuthenticationBldr;
局部的
AuthenticationManager
是和每一个HttpSecurity
对象绑定的,而全局的AuthenticationManager
对象则是所有局部AuthenticationManager
的parent
。
需要注意的是,localConfigureAuthenticationBldr
并非总是有用,在开发者没有重写configure(AuthenticationManagerBuilder)
方法的情况下,全局的AuthenticationManager
对象是由AuthenticationConfiguration
类中的getAuthenticationManager
方法提供的,如果用户重写了configure(AuthenticationManagerBuilder)
方法,则全局的AuthenticationManager
就由localConfigureAuthenticationBldr
负责构建。WebSecurityConfigurerAdapter
类的初始化方法如下:
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
// 创建并配置一个HttpSecurity的实例,之后添加到WebSecurity中
@Override
public void init(WebSecurity web) throws Exception {
// 获取一个HttpSecurity实例
HttpSecurity http = getHttp();
// 添加到WebSecurity中
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
protected final HttpSecurity getHttp() throws Exception {
// 如果已经初始化则直接返回
if (this.http != null) {
return this.http;
}
// 给localConfigureAuthenticationBldr设置事件发布器
AuthenticationEventPublisher eventPublisher = getAuthenticationEventPublisher();
this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
// 获取全局的AuthenticationManager对象
AuthenticationManager authenticationManager = authenticationManager();
this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
// 创建HttpSecurity实例
this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
if (!this.disableDefaults) {
// 应用默认配置,添加默认的过滤器
applyDefaultConfiguration(this.http);
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
this.http.apply(configurer);
}
}
// 进行扩展配置
configure(this.http);
return this.http;
}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().authenticated());
http.formLogin();
http.httpBasic();
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
protected AuthenticationManager authenticationManager() throws Exception {
if (!this.authenticationManagerInitialized) {
// 如果全局的AuthenticationManager对象还没有初始化,则先调用configure方法
configure(this.localConfigureAuthenticationBldr);
if (this.disableLocalConfigureAuthenticationBldr) {
// 通过调用getAuthenticationManager方法获取全局的AuthenticationManager对象并返回
this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager();
}
else {
// 如果开发者重写了configure(AuthenticationManagerBuilder)方法,
// 则disableLocalConfigureAuthenticationBldr变量就一直是false
this.authenticationManager = this.localConfigureAuthenticationBldr.build();
}
this.authenticationManagerInitialized = true;
}
return this.authenticationManager;
}
}
4.1.6初始化流程分析
在spring boot中使用spring security,初始化就从spring security的自动化配置类中开始:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
// 导入配置类(前三个为主)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class, ErrorPageSecurityFilterConfiguration.class })
public class SecurityAutoConfiguration {
// 定义默认的事件发布器
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
SpringBootWebSecurityConfiguration
:主要作用是在开发者没有提供WebSecurityConfigurerAdapter
实例的情况下,由其负责提供一个默认的实例,即导入web安全的默认配置。如果开发者指定了自己的WebSecurityConfigurerAdapter
或者SecurityFilterChain
,这将会失效。SecurityDataConfiguration
:主要提供了一个SecurityEvaluationContextExtension
实例,以便通过SpEL为经过身份验证的用户提供数据查询(需要引入相关依赖才能使用)。WebSecurityEnablerConfiguration
:分析的重点。ErrorPageSecurityFilterConfiguration
:配置ErrorPageSecurityFilter
。
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnClass(EnableWebSecurity.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity // 该注解引入了关键的配置类WebSecurityConfiguration
class WebSecurityEnablerConfiguration {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
HttpSecurityConfiguration.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
boolean debug() default false;
}
WebSecurityConfiguration
:用来配置WebSecurity
(重点分析)。SpringWebMvcImportSelector
:判断当前环境是否存在spring MVC,如果存在,则引入相关配置。OAuth2ImportSelector
:判断当前环境是否存在OAuth2,如果存在,则引入相关配置。HttpSecurityConfiguration
:公开HttpSecurity
bean的配置。另外还有一个
@EnableGlobalAuthentication
注解,用来开启全局配置:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(AuthenticationConfiguration.class) // 导入配置类AuthenticationConfiguration
@Configuration
public @interface EnableGlobalAuthentication {
}
可以看到,spring security的自动化配置类主要导入了两个类:
WebSecurityConfiguration
和AuthenticationConfiguration
。
WebSecurityConfiguration
主要功能就是为了构建spring security过滤器链代理对象
FilterChainProxy
。在WebSecurityConfiguration
中会首先构建WebSecurity
对象,再利用它来构建FilterChainProxy
。
先来看下其中定义的属性:
// 通过实现ImportAware接口可以方便地获取到@EnableWebSecurity中的属性值,即debug
// 通过实现BeanClassLoaderAware接口可以方便地获取到ClassLoader对象
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
// 需要构建的WebSecurity对象
private WebSecurity webSecurity;
private Boolean debugEnabled;
// 保存了所有的配置类,也就是WebSecurityConfigurerAdapter对象,一个WebSecurityConfigurerAdapter对象可以创建
// 一个HttpSecurity,进而构建出一条过滤器链,多个WebSecurityConfigurerAdapter对象就可以构建出多条过滤器链
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private ClassLoader beanClassLoader;
// 对象后置处理器,注意这个对象是直接从spring容器中注入的
@Autowired(required = false)
private ObjectPostProcessor<Object> objectObjectPostProcessor;
}
setFilterChainProxySecurityConfigurer
主要用来构建一个WebSecurity
对象,并且加载所有的配置类对象:
/**
* 设置用于创建web配置的<SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>实例,初始化WebSecurity对象,
* 同时收集到所有的自定义配置类。
* objectPostProcessor是一个对象后置处理器,由于该方法有一个@Autowired注解,会自动查找需要注入的参数;
* required属性为false即在方法参数注入的时候,有就注入,没有则忽略,主要是针对第二个参数。
*/
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
// 去对象后置处理器中进行处理,这样就将WebSecurity对象注册到spring容器中了
this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if (this.debugEnabled != null) {
this.webSecurity.debug(this.debugEnabled);
}
// 根据每一个配置类的@Order注解对集合中的所有配置类进行排序,因为一个配置类对应一个过滤器链,当请求到来后,
// 需要先和哪个过滤器链进行匹配,这里必然存在一个优先级问题。所以如果开发者自定义了多个配置类,则需要通过
// @Order注解标记多个配置类的优先级
webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
// 检查是否存在优先级相等的配置类
if (previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order
+ " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
}
previousOrder = order;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
// 将配置类添加到WebSecurity父类中的configurers集合中,将来遍历该集合并分别调用配置类的init和configure方法
// 完成配置类的初始化操作
this.webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
// 获取spring容器中所有的WebSecurityConfigurer实例,也就是开发者自定义的各种各样
// 继承自WebSecurityConfigurerAdapter的配置类,如果没有自定义,则获取默认的配置类
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<>();
Map<String, WebSecurityConfigurer> beansOfType = this.beanFactory.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
// 有了WebSecurity对象和配置类,接下来就可以构建过滤器FilterChainProxy了
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
// 如果不存在配置类且不存在过滤器链,则创建一个匿名的WebSecurityConfigurerAdapter对象并注册到spring容器中
if (!hasConfigurers && !hasFilterChain) {
WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
// securityFilterChains通过SecurityAutoConfiguration中导入SpringBootWebSecurityConfiguration进行配置
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);
}
// 调用build方法进行构建
return this.webSecurity.build();
}
WebSecurity
对象的build
方法执行后,首先会对所有的配置类即WebSecurityConfigurerAdapter
实例进行构建,在WebSecurityConfigurerAdapter
的init
方法中,又会完成HttpSecurity
的构建,而HttpSecurity
的构建过程中,则会完成局部AuthenticationManager
对象以及每一个具体的过滤器的构建。
这就是整个过滤器链的构建流程。
AuthenticationConfiguration
主要是做全局的配置,同时提供一个全局的
AuthenticationManager
实例(仅列出核心代码):
/**
* 如果开发者自定义配置类中重写了configure(AuthenticationManagerBuilder)方法,
* 这里的全局AuthenticationManager对象将不会生效,而大部分情况下都会重写。
*/
@Configuration(proxyBeanMethods = false)
// 导入配置类ObjectPostProcessorConfiguration,提供一个基本的对象后置处理器AutowireBeanFactoryObjectPostProcessor,
// 用来将一个对象注册到spring容器中,在其他配置类中所见到的ObjectPostProcessor实例其实都是这里提供的
@Import(ObjectPostProcessorConfiguration.class)
public class AuthenticationConfiguration {
/**
* 定义一个AuthenticationManagerBuilder实例,用来构建全局的AuthenticationManager对象,这个过程
* 会从spring容器中查找AuthenticationEventPublisher实例设置给AuthenticationManagerBuilder对象。
*/
@Bean
public AuthenticationManagerBuilder authenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor,
ApplicationContext context) {
LazyPasswordEncoder defaultPasswordEncoder = new LazyPasswordEncoder(context);
AuthenticationEventPublisher authenticationEventPublisher = getBeanOrNull(context,
AuthenticationEventPublisher.class);
DefaultPasswordEncoderAuthenticationManagerBuilder result = new DefaultPasswordEncoderAuthenticationManagerBuilder(
objectPostProcessor, defaultPasswordEncoder);
if (authenticationEventPublisher != null) {
result.authenticationEventPublisher(authenticationEventPublisher);
}
return result;
}
@Bean
public static GlobalAuthenticationConfigurerAdapter enableGlobalAuthenticationAutowiredConfigurer(
ApplicationContext context) {
// 用来从spring容器中加载被@EnableGlobalAuthentication注解标记的bean
return new EnableGlobalAuthenticationAutowiredConfigurer(context);
}
@Bean
public static InitializeUserDetailsBeanManagerConfigurer initializeUserDetailsBeanManagerConfigurer(
ApplicationContext context) {
// 用来初始化全局的UserDetailsService对象
return new InitializeUserDetailsBeanManagerConfigurer(context);
}
@Bean
public static InitializeAuthenticationProviderBeanManagerConfigurer initializeAuthenticationProviderBeanManagerConfigurer(
ApplicationContext context) {
// 用来初始化全局的AuthenticationProvider对象
return new InitializeAuthenticationProviderBeanManagerConfigurer(context);
}
// 构建具体的AuthenticationManager对象
public AuthenticationManager getAuthenticationManager() throws Exception {
// 判断AuthenticationManager对象是否已经初始化
if (this.authenticationManagerInitialized) {
return this.authenticationManager;
}
// 从容器中获取到AuthenticationManagerBuilder对象
AuthenticationManagerBuilder authBuilder = this.applicationContext.getBean(AuthenticationManagerBuilder.class);
// AuthenticationManagerDelegator用来防止在初始化AuthenticationManager时进行无限递归
if (this.buildingAuthenticationManager.getAndSet(true)) {
return new AuthenticationManagerDelegator(authBuilder);
}
// 遍历globalAuthConfigurers配置类集合,也就是上面带有@Bean注解的三个配置类
for (GlobalAuthenticationConfigurerAdapter config : this.globalAuthConfigurers) {
authBuilder.apply(config);
}
this.authenticationManager = authBuilder.build();
if (this.authenticationManager == null) {
this.authenticationManager = getAuthenticationManagerBean();
}
this.authenticationManagerInitialized = true;
return this.authenticationManager;
}
}
这就是全局的
AuthenticationManager
的构建过程。
整体来说,AuthenticationConfiguration
的作用主要体现在两方面:第一就是导入了ObjectPostProcessorConfiguration
配置类;第二则是提供了一个全局的AuthenticationManager
对象。
如果开发者在自定义配置类中重写了configure(AuthenticationManagerBuilder)
方法,这里的全局AuthenticationManager
对象将不会生效,而大部分情况下,开发者都会重写该方法。
4.2ObjectPostProcessor
使用
所有的过滤器都由对应的配置类来负责创建,配置类在将过滤器创建成功之后,会调用父类的
postProcess
方法,在该方法中,会遍历CompositeObjectPostProcessor
对象所维护的集合中存储的所有ObjectPostProcessor
对象,并调用其postProcess
方法对对象进行后置处理。默认情况下,CompositeObjectPostProcessor
对象中所维护的集合中只有一个对象那就是AutowireBeanFactoryObjectPostProcessor
,调用其postProcess
方法可以将对象注册到spring容器中去。
开发者可以自定义ObjectPostProcessor
对象,并添加到CompositeObjectPostProcessor
所维护的集合中,此时,当一个过滤器在创建成功之后,就会被两个对象后置处理器处理,第一个是默认的对象后置处理器,负责将对象注册到spring容器中;第二个是自定义的对象后置处理器,可以完成一些个性化配置。
自定义ObjectPostProcessor
对象比较典型的用法是动态权限配置。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
// 调用formLogin方法之后,开启了FormLoginConfigurer的配置,其作用是为了配置
// UsernamePasswordAuthenticationFilter过滤器,在formLogin方法执行完毕后,
// 调用withObjectPostProcessor方法对UsernamePasswordAuthenticationFilter进行二次处理
.formLogin()
.withObjectPostProcessor(new ObjectPostProcessor<UsernamePasswordAuthenticationFilter>() {
@Override
public <O extends UsernamePasswordAuthenticationFilter> O postProcess(O object) {
// 修改登录参数的key
object.setUsernameParameter("name");
object.setPasswordParameter("passwd");
// 同时配置一个登录成功的处理器
object.setAuthenticationSuccessHandler((request, response, authentication) -> {
response.getWriter().write("login success");
});
return object;
}
})
.and()
.csrf().disable();
}
}
4.3多种用户定义方式
目前来说,定义用户主要是两种方式:
- 重写
configure(AuthenticationManagerBuilder)
方法的方式。- 直接向spring容器中注入
UserDetailsService
对象。那么,这两种用户定义方式有什么区别?
在spring security中存在两种类型的AuthenticationManager
,一种是全局的,另一种则是局部的。局部的AuthenticationManager
由HttpSecurity
进行配置,而全局的则可以不用配置,系统会默认提供一个全局的AuthenticationManager
对象,也可以通过重写configure(AuthenticationManagerBuilder)
方法进行全局配置。
当进行用户身份验证时,首先会通过局部的AuthenticationManager
对象进行验证,如果验证失败,则会调用其parent
也就是全局的对象再次进行验证。
所以开发者在定义用户时,也分为两种情况,一种是针对局部AuthenticationManager
定义的用户,另一种则是针对全局AuthenticationManager
定义的用户。
先来看看针对局部AuthenticationManager
定义的用户(此时控制台仍会打印默认用户的密码):
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 针对局部的AuthenticationManager定义的用户。需要注意的是,此时系统仍会自动生成全局用户。系统自动
* 提供的用户对象实际上就是往spring容器中注册了一个InMemoryUserDetailsManager对象。而由于没有重写
* configure(AuthenticationManagerBuilder)方法,这意味着,全局的AuthenticationManager是通过
* AuthenticationConfiguration#getAuthenticationManager方法自动生成的,在生成的过程中,会从
* spring容器中查找对应的UserDetailsService实例进行配置(具体配置在InitializeUserDetailsManagerConfigurer中)。
* 当开始执行登录后,会先调用局部AuthenticationManager去进行登录校验,如果登录的用户名/密码是javaboy/123,
* 那就直接登录成功,否则登录失败。当登录失败后,会继续调用局部AuthenticationManager的parent继续进行校验。
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 基于内存来管理用户
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("javaboy").password("{noop}123").roles("admin").build());
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
// 配置到局部的AuthenticationManager中
.userDetailsService(manager)
.csrf().disable();
}
}
也可以将定义的用户配置给全局的
AuthenticationManager
,由于默认的全局AuthenticationManager
在配置时会从spring容器中查找UserDetailsService
实例,所以如果针对全局AuthenticationManager
配置用户,只需要往spring容器中注入一个UserDetailsService
实例即可。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 全局的
@Bean
UserDetailsService us() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("江南一点雨").password("{noop}123").roles("admin").build());
return manager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 局部的
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("javaboy").password("{noop}123").roles("admin").build());
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.userDetailsService(manager)
.csrf().disable();
}
}
当然,开发者也可以不使用spring security提供的默认的全局
AuthenticationManager
对象,而是通过重写configure(AuthenticationManagerBuilder)
方法来自定义全局AuthenticationManager
对象:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("javagirl").password("{noop}123").roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("javaboy").password("{noop}123").roles("admin").build());
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.userDetailsService(manager)
.csrf().disable();
}
}
一旦重写了
configure(AuthenticationManagerBuilder)
方法,则全局的AuthenticationManager
对象将不再通过AuthenticationConfiguration#getAuthenticationManager
方法来构建,而是通过WebSecurityConfigurerAdapter
中的localConfigureAuthenticationBldr
变量来构建,该变量也是重写的configure(AuthenticationManagerBuilder)
方法的参数。
需要注意的是,一旦重写了configure(AuthenticationManagerBuilder)
方法,那么全局的AuthenticationManager
对象中使用的用户,将以configure(AuthenticationManagerBuilder)
方法中定义的用户为准。此时,如果还向spring容器中注入了另外一个UserDetailsService
实例,那么该实例中定义的用户将不会生效(因为AuthenticationConfiguration#getAuthenticationManager
方法没有被调用)。
4.4定义多个过滤器链
在spring security中可以同时存在多个过滤器链,一个
WebSecurityConfigurerAdapter
的实例就可以配置一条过滤器链。
// 可以使用postman进行测试
@Configuration
public class SecurityConfig {
@Bean
UserDetailsService us() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("javaboy").password("{noop}123").roles("admin").build());
return manager;
}
@Configuration
@Order(1)
static class SecurityConfig01 extends WebSecurityConfigurerAdapter {
/**
* 由于SecurityConfig01中只是重写了configure(HttpSecurity http),所以注册到spring容器中的
* UserDetailsService(us)将作为局部Authentication的parent对应的用户。所以,如果登录的路径是/bar/login,
* 那么可以使用bar/123和javaboy/123两个用户进行登录。
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("bar").password("{noop}123").roles("admin").build());
http.antMatcher("/bar/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/bar/login")
.successHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
String s = new ObjectMapper().writeValueAsString(authentication);
response.getWriter().write(s);
})
.permitAll()
.and()
.csrf().disable()
.userDetailsService(manager);
}
}
@Configuration
@Order(2)
static class SecurityConfig02 extends WebSecurityConfigurerAdapter {
/**
* 由于重写了configure(AuthenticationManagerBuilder auth),在该方法中定义了局部AuthenticationManager
* 的parent对应的用户,此时注册到spring容器中的UserDetailsService(us)实例对于/foo/**过滤器链不再生效。
* 换句话说,如果登录路径是/foo/login,可以使用foo/123和javagirl/123两个用户进行登录,而不可以使用javaboy/123。
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("javagirl").password("{noop}123").roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("foo").password("{noop}123").roles("admin").build());
http.antMatcher("/foo/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/foo/login")
.successHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
String s = new ObjectMapper().writeValueAsString(authentication);
response.getWriter().write(s);
})
.permitAll()
.and()
.csrf().disable()
.userDetailsService(manager);
}
}
}
需要注意的是,如果配置了多个过滤器链,需要使用
@Order
注解来标记不同配置的优先级(即不同过滤器链的优先级),数字越大优先级越低。当请求到来时,会按照过滤器链的优先级从高往低,依次进行匹配。
4.5静态资源过滤
WebSecurity
中维护了一个ignoredRequests
变量,该变量记录的就是所有需要被忽略的请求,这些被忽略的请求将不再经过spring security过滤器。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 忽略的地址,最终都会被添加到ignoredRequests集合中,并最终以过滤器链的形式呈现出来(详见WebSecurity#performBuild
* 方法)。换句话说,重写的方法中一共包含了五个过滤器链:
* configure(WebSecurity web)中配置的四个以及configure(HttpSecurity http)中配置的一个(即/**,所有请求都需要进行
* 身份验证过滤)。
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/login.html", "/css/**", "/js/**", "/images/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable();
}
}
4.6使用JSON格式登录
登录参数的提取是在
UsernamePasswordAuthenticationFilter
过滤器中完成的。如果要使用JSON格式登录,只需要模仿它定义一个自己的过滤器,再将自定义的过滤器放到UsernamePasswordAuthenticationFilter
过滤器所在的位置(需要在之前进行验证)即可。
首先自定义一个LoginFilter
继承自UsernamePasswordAuthenticationFilter
:
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
// 父类通过调用子类的attemptAuthentication方法进行身份验证,因此需要重写该方法
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// 首先确保进入该过滤器中的请求是POST请求
if (!"POST".equals(request.getMethod())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
// 如果是JSON格式登录
if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(request.getContentType()) || MediaType.APPLICATION_JSON_UTF8_VALUE.equalsIgnoreCase(request.getContentType())) {
Map userInfo = new HashMap<>();
try {
userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);
String username = userInfo.get(getUsernameParameter()).toString();
String password = userInfo.get(getPasswordParameter()).toString();
// 使用提取出来的用户名/密码信息构造出UsernamePasswordAuthenticationToken对象
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
setDetails(request, authRequest);
// 执行认证
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IOException e) {
e.printStackTrace();
}
}
// 如果不是JSON格式登录,则调用父类方法继续进行身份验证
return super.attemptAuthentication(request, response);
}
}
定义完成之后,将其添加到spring security过滤器链中:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("javaboy").password("{noop}123").roles("admin");
}
// 重写父类的authenticationManagerBean方法来提供一个AuthenticationManager实例,并配置给LoginFilter
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
// 配置loginFilter实例,同时将AuthenticationManager实例设置给loginFilter,然后再设置登录成功回调
@Bean
LoginFilter loginFilter() throws Exception {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setAuthenticationManager(authenticationManagerBean());
loginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(new ObjectMapper().writeValueAsString(authentication));
});
return loginFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable();
// 在UsernamePasswordAuthenticationFilter所在的位置添加自定义的过滤器进行处理,如果不能处理,
// 则通过super调用UsernamePasswordAuthenticationFilter进行处理
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
当想要获取一个
AuthenticationManager
实例时,有两种方式:
- 第一种是通过重写父类的
authenticationManager
方法获取。- 第二种则是通过重写父类的
authenticationManagerBean
方法获取。两者的区别在于第一种获取到的是全局的
AuthenticationManager
实例,而第二种获取到的则是局部的实例。而LoginFilter
作为过滤器链中的一环,显然应该配置局部的实例,因为如果将全局的实例配置给LoginFilter
,则局部AuthenticationManager
实例所对应的用户就会失效。
/**
* 无法使用javagirl/123进行登录,因为LoginFilter中指定了全局的AuthenticationManager来做验证,
* 所以局部的AuthenticationManager失效了
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("javaboy").password("{noop}123").roles("admin");
}
@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
LoginFilter loginFilter() throws Exception {
LoginFilter loginFilter = new LoginFilter();
loginFilter.setAuthenticationManager(authenticationManager());
loginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(new ObjectMapper().writeValueAsString(authentication));
});
return loginFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
InMemoryUserDetailsManager users = new InMemoryUserDetailsManager();
users.createUser(User.withUsername("javagirl").password("{noop}123").roles("admin").build());
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.userDetailsService(users);
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
在实际应用中,如果需要自己配置一个
AuthenticationManager
实例,大部分情况下,都是通过重写authenticationManagerBean
方法来获取。
4.7添加登录验证码
前面介绍了一种登录验证码的实现方案,其实通过过滤器链来实现会更加容易。验证码的生成方案和之前的一致,主要介绍一下
LoginFilter
的定义以及配置:
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!"POST".equalsIgnoreCase(request.getMethod())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String kaptcha = request.getParameter("kaptcha");
String sessionKaptcha = (String) request.getSession().getAttribute("kaptcha");
if (StringUtils.hasLength(kaptcha) && StringUtils.hasLength(sessionKaptcha) && kaptcha.equalsIgnoreCase(sessionKaptcha)) {
return super.attemptAuthentication(request, response);
}
throw new AuthenticationServiceException("验证码输入错误");
}
}
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("javaboy").password("{noop}123").roles("admin");
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
LoginFilter loginFilter() throws Exception {
LoginFilter loginFilter = new LoginFilter();
// 等价于loginProcessingUrl
loginFilter.setFilterProcessesUrl("/doLogin");
loginFilter.setAuthenticationManager(authenticationManagerBean());
loginFilter.setAuthenticationSuccessHandler(new SimpleUrlAuthenticationSuccessHandler("/hello"));
loginFilter.setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler("/login.html"));
// 如果继承了UsernamePasswordAuthenticationFilter,则相关的配置项需要LoginFilter实例上配置
// loginFilter.setUsernameParameter("uname");
// loginFilter.setPasswordParameter("passwd");
return loginFilter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/vc.jpg").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
// 继承UsernamePasswordAuthenticationFilter之后,这个地方的配置无效
// .usernameParameter("uname")
// .passwordParameter("passwd")
.permitAll()
.and()
.csrf().disable();
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
可以看到,相比于第一种方式,这种更加简单也更易于理解。