Spring Security 6.x 系列(10)—— SecurityConfigurer 配置器及其分支实现源码分析(二)
一、前言
在本系列文章:
Spring Security 6.x 系列(4)—— 基于过滤器链的源码分析(一)
中着重分析了Spring Security
在Spring Boot
自动配置、 DefaultSecurityFilterChain
和FilterChainProxy
的构造过程。
Spring Security 6.x 系列(7)—— SecurityBuilder 继承链源码分析
中详细分析了Spring Security
中WebSecurity
、HttpSecurity
、AuthenticationManagerBuilder
三个构造器的公共继承链。
Spring Security 6.x 系列(8)—— SecurityConfigurer 配置器及其分支实现源码分析(一)
中分析SecurityConfigurer
配置器及其主要分支实现。
Spring Security 6.x 系列(9)—— 基于过滤器链的源码分析(二)
着重分析了@EnableGlobalAuthentication
注解的作用、对AuthenticationConfiguration
构造AuthenticationManager
过程和上文中未介绍的GlobalAuthenticationConfigurerAdapter
配置器的五个分支实现进行了详细的说明。
今天我们就从未被介绍的SecurityConfigurerAdapter
配置器的具体分支实现进行展开。
二、SecurityConfigurerAdapter
SecurityConfigurerAdapter
在上文中有过详解介绍,它是SecurityConfigurer
的基类,它允许子类仅实现它们感兴趣的方法。它还提供了使用 SecurityConfigurer
以及完成后获取正在配置的SecurityBuilder
(构造器)的访问权限的机制。
SecurityConfigurerAdapter
的实现主要有三大类:
UserDetailsAwareConfigurer
AbstractHttpConfigurer
LdapAuthenticationProviderConfigurer
考虑到 LDAP
现在使用很少,所以重点介绍前两个。
三、UserDetailsAwareConfigurer
从这个类名就能大概知道是和用户详细信息配置有关。
再通过继承关系图,看看UserDetailsAwareConfigurer
的顶层架构设计:
UserDetailsAwareConfigurer
是一个抽象类,源码比较简单:
/**
* Base class that allows access to the {@link UserDetailsService} for using as a default
* value with {@link AuthenticationManagerBuilder}.
*
* @param <B> the type of the {@link ProviderManagerBuilder}
* @param <U> the type of {@link UserDetailsService}
* @author Rob Winch
*/
public abstract class UserDetailsAwareConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>
extends SecurityConfigurerAdapter<AuthenticationManager, B> {
/**
* Gets the {@link UserDetailsService} or null if it is not available
* @return the {@link UserDetailsService} or null if it is not available
*/
public abstract U getUserDetailsService();
}
通过源码我们可知:
-
泛型
U
继承了UserDetailsService
接口,也就意味着
getUserDetailsService()
方法返回的对象肯定是UserDetailsService
接口的实现。 -
泛型
B
继承了ProviderManagerBuilder
接口,ProviderManagerBuilder
构造器的作用是用来构建AuthenticationManager
对象,可就意味UserDetailsAwareConfigurer
(配置器)用来配置ProviderManagerBuilder
构造器。
3.1 AbstractDaoAuthenticationConfigurer
AbstractDaoAuthenticationConfigurer
也是一个抽象类,是模版模式:
/**
* Allows configuring a {@link DaoAuthenticationProvider}
*
* @param <B> the type of the {@link SecurityBuilder}
* @param <C> the type of {@link AbstractDaoAuthenticationConfigurer} this is
* @param <U> The type of {@link UserDetailsService} that is being used
* @author Rob Winch
* @since 3.2
*/
public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, C extends AbstractDaoAuthenticationConfigurer<B, C, U>, U extends UserDetailsService>
extends UserDetailsAwareConfigurer<B, U> {
// 声明了一个 provider
private DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
// 声明了一个 userDetailsService 的泛型属性
private final U userDetailsService;
/**
* 创建一个实例
* @param userDetailsService,userDetailsService的类型可以是UserDetailsService或者UserDetailsPasswordService
*/
AbstractDaoAuthenticationConfigurer(U userDetailsService) {
this.userDetailsService = userDetailsService;
this.provider.setUserDetailsService(userDetailsService);
if (userDetailsService instanceof UserDetailsPasswordService) {
this.provider.setUserDetailsPasswordService((UserDetailsPasswordService) userDetailsService);
}
}
/**
* Adds an {@link ObjectPostProcessor} for this class.
* @param objectPostProcessor
* @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations
*/
@SuppressWarnings("unchecked")
public C withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (C) this;
}
/**
* Allows specifying the {@link PasswordEncoder} to use with the
* {@link DaoAuthenticationProvider}. The default is to use plain text.
* @param passwordEncoder The {@link PasswordEncoder} to use.
* @return the {@link AbstractDaoAuthenticationConfigurer} for further customizations
*/
@SuppressWarnings("unchecked")
public C passwordEncoder(PasswordEncoder passwordEncoder) {
this.provider.setPasswordEncoder(passwordEncoder);
return (C) this;
}
public C userDetailsPasswordManager(UserDetailsPasswordService passwordManager) {
this.provider.setUserDetailsPasswordService(passwordManager);
return (C) this;
}
@Override
public void configure(B builder) throws Exception {
this.provider = postProcess(this.provider);
// 向builder添加provider(配置构造器阶段)
builder.authenticationProvider(this.provider);
}
/**
* Gets the {@link UserDetailsService} that is used with the
* {@link DaoAuthenticationProvider}
* @return the {@link UserDetailsService} that is used with the
* {@link DaoAuthenticationProvider}
*/
@Override
public U getUserDetailsService() {
return this.userDetailsService;
}
}
通过源码我们可知:
AbstractDaoAuthenticationConfigurer
初始时创建了一个DaoAuthenticationProvider
类型的AuthenticationProvider
实例。- 为使用者提供设置
DaoAuthenticationProvider
属性UserDetailsService
的功能并指定类型为:UserDetailsService/U serDetailsPasswordService
。 - 为使用者提供设置
DaoAuthenticationProvider
属性PasswordEncoder
功能; - 为使用者提供设置对象后置处处理器的功能。
AbstractDaoAuthenticationConfigurer
配置构造器对应的初始化阶段方法为空。AbstractDaoAuthenticationConfigurer
配置构造器对应的配置阶段方法:- 对
DaoAuthenticationProvider
执行后置处理 - 将
DaoAuthenticationProvider
添加到构造器中
- 对
3.2 DaoAuthenticationConfigurer
DaoAuthenticationConfigurer
继承自 AbstractDaoAuthenticationConfigurer
,在构造方法中调用AbstractDaoAuthenticationConfigurer
的构造方法。
/**
* Allows configuring a {@link DaoAuthenticationProvider}
*
* @param <B> The type of {@link ProviderManagerBuilder} this is
* @param <U> The type of {@link UserDetailsService} that is being used
* @author Rob Winch
* @since 3.2
*/
public class DaoAuthenticationConfigurer<B extends ProviderManagerBuilder<B>, U extends UserDetailsService>
extends AbstractDaoAuthenticationConfigurer<B, DaoAuthenticationConfigurer<B, U>, U> {
/**
* Creates a new instance
* @param userDetailsService
*/
public DaoAuthenticationConfigurer(U userDetailsService) {
super(userDetailsService);
}
}
3.3 UserDetailsServiceConfigurer
UserDetailsServiceConfigurer
继承了AbstractDaoAuthenticationConfigurer
,并重写了AbstractDaoAuthenticationConfigurer
中的configure
方法,在configure
方法执行之前加入了initUserDetailsService
方法,以方便开发时按照自己的方式去初始化 UserDetailsService
。这里的initUserDetailsService
方法是空的,会交于子类进行具体实现,也是模版模式。
/**
* Allows configuring a {@link UserDetailsService} within a
* {@link AuthenticationManagerBuilder}.
*
* @param <B> the type of the {@link ProviderManagerBuilder}
* @param <C> the {@link UserDetailsServiceConfigurer} (or this)
* @param <U> the type of UserDetailsService being used to allow for returning the
* concrete UserDetailsService.
* @author Rob Winch
* @since 3.2
*/
public class UserDetailsServiceConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsServiceConfigurer<B, C, U>, U extends UserDetailsService>
extends AbstractDaoAuthenticationConfigurer<B, C, U> {
/**
* Creates a new instance
* @param userDetailsService the {@link UserDetailsService} that should be used
*/
public UserDetailsServiceConfigurer(U userDetailsService) {
super(userDetailsService);
}
@Override
public void configure(B builder) throws Exception {
initUserDetailsService();
super.configure(builder);
}
/**
* Allows subclasses to initialize the {@link UserDetailsService}. For example, it
* might add users, initialize schema, etc.
*/
protected void initUserDetailsService() throws Exception {
}
}
3.4 UserDetailsManagerConfigurer
UserDetailsManagerConfigurer
继承了UserDetailsServiceConfigurer
,并实现了 UserDetailsServiceConfigurer
中定义的initUserDetailsService
空方法,具体的实现逻辑就是将UserDetailsBuilder
所构建出来的 UserDetails
以及提前准备好的UserDetails
中的用户存储到UserDetailsService
中。
在实例构造上进一步限制了父类中的U userDetailsService
的类型为UserDetailsManager
。
该类同时添加 withUser
方法用来添加用户,同时还增加了一个UserDetailsBuilder
用来构建用户,这些逻辑都比较简单,大家可以自行查看。
/**
* Base class for populating an
* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}
* with a {@link UserDetailsManager}.
*
* @param <B> the type of the {@link SecurityBuilder} that is being configured
* @param <C> the type of {@link UserDetailsManagerConfigurer}
* @author Rob Winch
* @since 3.2
*/
public class UserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>, C extends UserDetailsManagerConfigurer<B, C>>
extends UserDetailsServiceConfigurer<B, C, UserDetailsManager> {
private final List<UserDetailsBuilder> userBuilders = new ArrayList<>();
private final List<UserDetails> users = new ArrayList<>();
protected UserDetailsManagerConfigurer(UserDetailsManager userDetailsManager) {
super(userDetailsManager);
}
/**
* Populates the users that have been added.
* @throws Exception
*/
@Override
protected void initUserDetailsService() throws Exception {
for (UserDetailsBuilder userBuilder : this.userBuilders) {
getUserDetailsService().createUser(userBuilder.build());
}
for (UserDetails userDetails : this.users) {
getUserDetailsService().createUser(userDetails);
}
}
/**
* Allows adding a user to the {@link UserDetailsManager} that is being created. This
* method can be invoked multiple times to add multiple users.
* @param userDetails the user to add. Cannot be null.
* @return the {@link UserDetailsBuilder} for further customizations
*/
@SuppressWarnings("unchecked")
public final C withUser(UserDetails userDetails) {
this.users.add(userDetails);
return (C) this;
}
/**
* Allows adding a user to the {@link UserDetailsManager} that is being created. This
* method can be invoked multiple times to add multiple users.
* @param userBuilder the user to add. Cannot be null.
* @return the {@link UserDetailsBuilder} for further customizations
*/
@SuppressWarnings("unchecked")
public final C withUser(User.UserBuilder userBuilder) {
this.users.add(userBuilder.build());
return (C) this;
}
/**
* Allows adding a user to the {@link UserDetailsManager} that is being created. This
* method can be invoked multiple times to add multiple users.
* @param username the username for the user being added. Cannot be null.
* @return the {@link UserDetailsBuilder} for further customizations
*/
@SuppressWarnings("unchecked")
public final UserDetailsBuilder withUser(String username) {
UserDetailsBuilder userBuilder = new UserDetailsBuilder((C) this);
userBuilder.username(username);
this.userBuilders.add(userBuilder);
return userBuilder;
}
/**
* Builds the user to be added. At minimum the username, password, and authorities
* should provided. The remaining attributes have reasonable defaults.
*/
public final class UserDetailsBuilder {
private UserBuilder user;
private final C builder;
/**
* Creates a new instance
* @param builder the builder to return
*/
private UserDetailsBuilder(C builder) {
this.builder = builder;
}
/**
* Returns the {@link UserDetailsManagerConfigurer} for method chaining (i.e. to
* add another user)
* @return the {@link UserDetailsManagerConfigurer} for method chaining
*/
public C and() {
return this.builder;
}
/**
* Populates the username. This attribute is required.
* @param username the username. Cannot be null.
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
private UserDetailsBuilder username(String username) {
this.user = User.withUsername(username);
return this;
}
/**
* Populates the password. This attribute is required.
* @param password the password. Cannot be null.
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserDetailsBuilder password(String password) {
this.user.password(password);
return this;
}
/**
* Populates the roles. This method is a shortcut for calling
* {@link #authorities(String...)}, but automatically prefixes each entry with
* "ROLE_". This means the following:
*
* <code>
* builder.roles("USER","ADMIN");
* </code>
*
* is equivalent to
*
* <code>
* builder.authorities("ROLE_USER","ROLE_ADMIN");
* </code>
*
* <p>
* This attribute is required, but can also be populated with
* {@link #authorities(String...)}.
* </p>
* @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null,
* contain null values or start with "ROLE_"
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserDetailsBuilder roles(String... roles) {
this.user.roles(roles);
return this;
}
/**
* Populates the authorities. This attribute is required.
* @param authorities the authorities for this user. Cannot be null, or contain
* null values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(GrantedAuthority... authorities) {
this.user.authorities(authorities);
return this;
}
/**
* Populates the authorities. This attribute is required.
* @param authorities the authorities for this user. Cannot be null, or contain
* null values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(List<? extends GrantedAuthority> authorities) {
this.user.authorities(authorities);
return this;
}
/**
* Populates the authorities. This attribute is required.
* @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN,
* etc). Cannot be null, or contain null values
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
* @see #roles(String...)
*/
public UserDetailsBuilder authorities(String... authorities) {
this.user.authorities(authorities);
return this;
}
/**
* Defines if the account is expired or not. Default is false.
* @param accountExpired true if the account is expired, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserDetailsBuilder accountExpired(boolean accountExpired) {
this.user.accountExpired(accountExpired);
return this;
}
/**
* Defines if the account is locked or not. Default is false.
* @param accountLocked true if the account is locked, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserDetailsBuilder accountLocked(boolean accountLocked) {
this.user.accountLocked(accountLocked);
return this;
}
/**
* Defines if the credentials are expired or not. Default is false.
* @param credentialsExpired true if the credentials are expired, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserDetailsBuilder credentialsExpired(boolean credentialsExpired) {
this.user.credentialsExpired(credentialsExpired);
return this;
}
/**
* Defines if the account is disabled or not. Default is false.
* @param disabled true if the account is disabled, false otherwise
* @return the {@link UserDetailsBuilder} for method chaining (i.e. to populate
* additional attributes for this user)
*/
public UserDetailsBuilder disabled(boolean disabled) {
this.user.disabled(disabled);
return this;
}
UserDetails build() {
return this.user.build();
}
}
}
3.5 JdbcUserDetailsManagerConfigurer
JdbcUserDetailsManagerConfigurer
继承了UserDetailsManagerConfigurer
,在父类的基础上补充了 DataSource
对象,同时还提供了相应的数据库查询方法。
/**
* Configures an
* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}
* to have JDBC authentication. It also allows easily adding users to the database used
* for authentication and setting up the schema.
*
* <p>
* The only required method is the {@link #dataSource(javax.sql.DataSource)} all other
* methods have reasonable defaults.
*
* @param <B> the type of the {@link ProviderManagerBuilder} that is being configured
* @author Rob Winch
* @since 3.2
*/
public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>
extends UserDetailsManagerConfigurer<B, JdbcUserDetailsManagerConfigurer<B>> {
private DataSource dataSource;
private List<Resource> initScripts = new ArrayList<>();
public JdbcUserDetailsManagerConfigurer(JdbcUserDetailsManager manager) {
super(manager);
}
public JdbcUserDetailsManagerConfigurer() {
this(new JdbcUserDetailsManager());
}
/**
* Populates the {@link DataSource} to be used. This is the only required attribute.
* @param dataSource the {@link DataSource} to be used. Cannot be null.
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource) {
this.dataSource = dataSource;
getUserDetailsService().setDataSource(dataSource);
return this;
}
/**
* Sets the query to be used for finding a user by their username. For example:
*
* <code>
* select username,password,enabled from users where username = ?
* </code>
* @param query The query to use for selecting the username, password, and if the user
* is enabled by username. Must contain a single parameter for the username.
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> usersByUsernameQuery(String query) {
getUserDetailsService().setUsersByUsernameQuery(query);
return this;
}
/**
* Sets the query to be used for finding a user's authorities by their username. For
* example:
*
* <code>
* select username,authority from authorities where username = ?
* </code>
* @param query The query to use for selecting the username, authority by username.
* Must contain a single parameter for the username.
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query) {
getUserDetailsService().setAuthoritiesByUsernameQuery(query);
return this;
}
/**
* An SQL statement to query user's group authorities given a username. For example:
*
* <code>
* select
* g.id, g.group_name, ga.authority
* from
* groups g, group_members gm, group_authorities ga
* where
* gm.username = ? and g.id = ga.group_id and g.id = gm.group_id
* </code>
* @param query The query to use for selecting the authorities by group. Must contain
* a single parameter for the username.
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> groupAuthoritiesByUsername(String query) {
JdbcUserDetailsManager userDetailsService = getUserDetailsService();
userDetailsService.setEnableGroups(true);
userDetailsService.setGroupAuthoritiesByUsernameQuery(query);
return this;
}
/**
* A non-empty string prefix that will be added to role strings loaded from persistent
* storage (default is "").
* @param rolePrefix
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> rolePrefix(String rolePrefix) {
getUserDetailsService().setRolePrefix(rolePrefix);
return this;
}
/**
* Defines the {@link UserCache} to use
* @param userCache the {@link UserCache} to use
* @return the {@link JdbcUserDetailsManagerConfigurer} for further customizations
*/
public JdbcUserDetailsManagerConfigurer<B> userCache(UserCache userCache) {
getUserDetailsService().setUserCache(userCache);
return this;
}
@Override
protected void initUserDetailsService() throws Exception {
if (!this.initScripts.isEmpty()) {
getDataSourceInit().afterPropertiesSet();
}
super.initUserDetailsService();
}
@Override
public JdbcUserDetailsManager getUserDetailsService() {
return (JdbcUserDetailsManager) super.getUserDetailsService();
}
/**
* Populates the default schema that allows users and authorities to be stored.
* @return The {@link JdbcUserDetailsManagerConfigurer} used for additional
* customizations
*/
public JdbcUserDetailsManagerConfigurer<B> withDefaultSchema() {
this.initScripts.add(new ClassPathResource("org/springframework/security/core/userdetails/jdbc/users.ddl"));
return this;
}
protected DatabasePopulator getDatabasePopulator() {
ResourceDatabasePopulator dbp = new ResourceDatabasePopulator();
dbp.setScripts(this.initScripts.toArray(new Resource[0]));
return dbp;
}
private DataSourceInitializer getDataSourceInit() {
DataSourceInitializer dsi = new DataSourceInitializer();
dsi.setDatabasePopulator(getDatabasePopulator());
dsi.setDataSource(this.dataSource);
return dsi;
}
}
在实例构造上进一步限制了父类中的U userDetailsService
的类型为JdbcUserDetailsManager
。
JdbcUserDetailsManager
的继承关系图:
3.6 InMemoryUserDetailsManagerConfigurer
InMemoryUserDetailsManagerConfigurer
继承了UserDetailsManagerConfigurer
,在实例构造上进一步限制了父类中的U userDetailsService
的类型为InMemoryUserDetailsManager
。
/**
* Configures an
* {@link org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder}
* to have in memory authentication. It also allows easily adding users to the in memory
* authentication.
*
* @param <B> the type of the {@link ProviderManagerBuilder} that is being configured
* @author Rob Winch
* @since 3.2
*/
public class InMemoryUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>
extends UserDetailsManagerConfigurer<B, InMemoryUserDetailsManagerConfigurer<B>> {
/**
* Creates a new instance
*/
public InMemoryUserDetailsManagerConfigurer() {
super(new InMemoryUserDetailsManager(new ArrayList<>()));
}
}
InMemoryUserDetailsManager
的继承关系图:
3.7 使用示例
3.7.1 InMemoryUserDetailsManagerConfigurer 使用
@Configuration
public class BaseAuthenticationConfig {
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER").and()
.withUser("admin").password("{noop}password").roles("USER", "ADMIN");
}
}
3.7.2 JdbcUserDetailsManagerConfigurer 使用
@Configuration
public class BaseAuthenticationConfig {
@Autowired
DataSource dataSource;
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
}
3.7.3 DaoAuthenticationConfigurer 使用
@Configuration
public class BaseAuthenticationConfig {
@Autowired
UserDetailsService userDetailsService;
@Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
四、AbstractHttpConfigurer
从这个类名就能大概知道是和Http
配置有关。
源码注释:
为在HttpSecurity
上运行的SecurityConfigurer
实例添加一个方便的基类。
/**
* Adds a convenient base class for {@link SecurityConfigurer} instances that operate on
* {@link HttpSecurity}.
*
* @author Rob Winch
*/
public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T, B>, B extends HttpSecurityBuilder<B>>
extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, B> {
private SecurityContextHolderStrategy securityContextHolderStrategy;
/**
* Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh
* version of the configuration can be applied.
* @return the {@link HttpSecurityBuilder} for additional customizations
*/
@SuppressWarnings("unchecked")
public B disable() {
getBuilder().removeConfigurer(getClass());
return getBuilder();
}
@SuppressWarnings("unchecked")
public T withObjectPostProcessor(ObjectPostProcessor<?> objectPostProcessor) {
addObjectPostProcessor(objectPostProcessor);
return (T) this;
}
protected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {
if (this.securityContextHolderStrategy != null) {
return this.securityContextHolderStrategy;
}
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
String[] names = context.getBeanNamesForType(SecurityContextHolderStrategy.class);
if (names.length == 1) {
this.securityContextHolderStrategy = context.getBean(SecurityContextHolderStrategy.class);
}
else {
this.securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
}
return this.securityContextHolderStrategy;
}
}
AbstractHttpConfigurer
是一个抽象类,继承了SecurityConfigurerAdapter
,是模版模式,通过源码我们可知:
-
继承
SecurityConfigurerAdapter
时限制了父类的构建对象类型为:DefaultSecurityFilterChain
。 -
泛型
B
继承了HttpSecurityBuilder
接口,HttpSecurityBuilder
构造器的作用是用来构建DefaultSecurityFilterChain
对象,可就意味AbstractHttpConfigurer
(配置器)用来配置HttpSecurityBuilder
构造器。 -
disable()
方法通过移除configurer
来实现禁用某配置器。 -
withObjectPostProcessor
()方法为配置器手动添加后置处理器。 -
getSecurityContextHolderStrategy
()方法获取SecurityContextHolder
的使用策略。
我们来看下AbstractHttpConfigurer
子类:
过滤器的配置都在这里面,我们可通过修改配置来实现自定义业务扩展。
下文我们着重分析下
AbstractAuthenticationFilterConfigurer
和FormLoginConfigurer
,其他子类实现在后续的业务实战章节我们再逐个进行分析。
4.1 AbstractAuthenticationFilterConfigurer
源码注释:
用于配置AbstractAuthenticationFilterConfigurer
的基类。 这仅供内部使用。
/**
* Base class for configuring {@link AbstractAuthenticationFilterConfigurer}. This is
* intended for internal use only.
*
* @param <T> refers to "this" for returning the current configurer
* @param <F> refers to the {@link AbstractAuthenticationProcessingFilter} that is being
* built
* @author Rob Winch
* @since 3.2
* @see FormLoginConfigurer
*/
public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecurityBuilder<B>, T extends AbstractAuthenticationFilterConfigurer<B, T, F>, F extends AbstractAuthenticationProcessingFilter>
extends AbstractHttpConfigurer<T, B> {
private F authFilter;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource;
private SavedRequestAwareAuthenticationSuccessHandler defaultSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationSuccessHandler successHandler = this.defaultSuccessHandler;
private LoginUrlAuthenticationEntryPoint authenticationEntryPoint;
private boolean customLoginPage;
private String loginPage;
private String loginProcessingUrl;
private AuthenticationFailureHandler failureHandler;
private boolean permitAll;
private String failureUrl;
/**
* Creates a new instance with minimal defaults
*/
protected AbstractAuthenticationFilterConfigurer() {
setLoginPage("/login");
}
/**
* Creates a new instance
* @param authenticationFilter the {@link AbstractAuthenticationProcessingFilter} to
* use
* @param defaultLoginProcessingUrl the default URL to use for
* {@link #loginProcessingUrl(String)}
*/
protected AbstractAuthenticationFilterConfigurer(F authenticationFilter, String defaultLoginProcessingUrl) {
this();
this.authFilter = authenticationFilter;
if (defaultLoginProcessingUrl != null) {
loginProcessingUrl(defaultLoginProcessingUrl);
}
}
/**
* Specifies where users will be redirected after authenticating successfully if they
* have not visited a secured page prior to authenticating. This is a shortcut for
* calling {@link #defaultSuccessUrl(String, boolean)}.
* @param defaultSuccessUrl the default success url
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T defaultSuccessUrl(String defaultSuccessUrl) {
return defaultSuccessUrl(defaultSuccessUrl, false);
}
/**
* Specifies where users will be redirected after authenticating successfully if they
* have not visited a secured page prior to authenticating or {@code alwaysUse} is
* true. This is a shortcut for calling
* {@link #successHandler(AuthenticationSuccessHandler)}.
* @param defaultSuccessUrl the default success url
* @param alwaysUse true if the {@code defaultSuccessUrl} should be used after
* authentication despite if a protected page had been previously visited
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) {
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
handler.setDefaultTargetUrl(defaultSuccessUrl);
handler.setAlwaysUseDefaultTargetUrl(alwaysUse);
this.defaultSuccessHandler = handler;
return successHandler(handler);
}
/**
* Specifies the URL to validate the credentials.
* @param loginProcessingUrl the URL to validate username and password
* @return the {@link FormLoginConfigurer} for additional customization
*/
public T loginProcessingUrl(String loginProcessingUrl) {
this.loginProcessingUrl = loginProcessingUrl;
this.authFilter.setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl));
return getSelf();
}
public T securityContextRepository(SecurityContextRepository securityContextRepository) {
this.authFilter.setSecurityContextRepository(securityContextRepository);
return getSelf();
}
/**
* Create the {@link RequestMatcher} given a loginProcessingUrl
* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the
* loginProcessingUrl
* @return the {@link RequestMatcher} to use based upon the loginProcessingUrl
*/
protected abstract RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl);
/**
* Specifies a custom {@link AuthenticationDetailsSource}. The default is
* {@link WebAuthenticationDetailsSource}.
* @param authenticationDetailsSource the custom {@link AuthenticationDetailsSource}
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T authenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
this.authenticationDetailsSource = authenticationDetailsSource;
return getSelf();
}
/**
* Specifies the {@link AuthenticationSuccessHandler} to be used. The default is
* {@link SavedRequestAwareAuthenticationSuccessHandler} with no additional properties
* set.
* @param successHandler the {@link AuthenticationSuccessHandler}.
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T successHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
return getSelf();
}
/**
* Equivalent of invoking permitAll(true)
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T permitAll() {
return permitAll(true);
}
/**
* Ensures the urls for {@link #failureUrl(String)} as well as for the
* {@link HttpSecurityBuilder}, the {@link #getLoginPage} and
* {@link #getLoginProcessingUrl} are granted access to any user.
* @param permitAll true to grant access to the URLs false to skip this step
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T permitAll(boolean permitAll) {
this.permitAll = permitAll;
return getSelf();
}
/**
* The URL to send users if authentication fails. This is a shortcut for invoking
* {@link #failureHandler(AuthenticationFailureHandler)}. The default is
* "/login?error".
* @param authenticationFailureUrl the URL to send users if authentication fails (i.e.
* "/login?error").
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T failureUrl(String authenticationFailureUrl) {
T result = failureHandler(new SimpleUrlAuthenticationFailureHandler(authenticationFailureUrl));
this.failureUrl = authenticationFailureUrl;
return result;
}
/**
* Specifies the {@link AuthenticationFailureHandler} to use when authentication
* fails. The default is redirecting to "/login?error" using
* {@link SimpleUrlAuthenticationFailureHandler}
* @param authenticationFailureHandler the {@link AuthenticationFailureHandler} to use
* when authentication fails.
* @return the {@link FormLoginConfigurer} for additional customization
*/
public final T failureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.failureUrl = null;
this.failureHandler = authenticationFailureHandler;
return getSelf();
}
@Override
public void init(B http) throws Exception {
updateAuthenticationDefaults();
updateAccessDefaults(http);
registerDefaultAuthenticationEntryPoint(http);
}
@SuppressWarnings("unchecked")
protected final void registerDefaultAuthenticationEntryPoint(B http) {
registerAuthenticationEntryPoint(http, this.authenticationEntryPoint);
}
@SuppressWarnings("unchecked")
protected final void registerAuthenticationEntryPoint(B http, AuthenticationEntryPoint authenticationEntryPoint) {
ExceptionHandlingConfigurer<B> exceptionHandling = http.getConfigurer(ExceptionHandlingConfigurer.class);
if (exceptionHandling == null) {
return;
}
exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint),
getAuthenticationEntryPointMatcher(http));
}
protected final RequestMatcher getAuthenticationEntryPointMatcher(B http) {
ContentNegotiationStrategy contentNegotiationStrategy = http.getSharedObject(ContentNegotiationStrategy.class);
if (contentNegotiationStrategy == null) {
contentNegotiationStrategy = new HeaderContentNegotiationStrategy();
}
MediaTypeRequestMatcher mediaMatcher = new MediaTypeRequestMatcher(contentNegotiationStrategy,
MediaType.APPLICATION_XHTML_XML, new MediaType("image", "*"), MediaType.TEXT_HTML,
MediaType.TEXT_PLAIN);
mediaMatcher.setIgnoredMediaTypes(Collections.singleton(MediaType.ALL));
RequestMatcher notXRequestedWith = new NegatedRequestMatcher(
new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
return new AndRequestMatcher(Arrays.asList(notXRequestedWith, mediaMatcher));
}
@Override
public void configure(B http) throws Exception {
PortMapper portMapper = http.getSharedObject(PortMapper.class);
if (portMapper != null) {
this.authenticationEntryPoint.setPortMapper(portMapper);
}
RequestCache requestCache = http.getSharedObject(RequestCache.class);
if (requestCache != null) {
this.defaultSuccessHandler.setRequestCache(requestCache);
}
this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
if (this.authenticationDetailsSource != null) {
this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
SessionAuthenticationStrategy sessionAuthenticationStrategy = http
.getSharedObject(SessionAuthenticationStrategy.class);
if (sessionAuthenticationStrategy != null) {
this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
this.authFilter.setRememberMeServices(rememberMeServices);
}
SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
SecurityContextRepository securityContextRepository = securityContextConfigurer
.getSecurityContextRepository();
this.authFilter.setSecurityContextRepository(securityContextRepository);
}
this.authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
F filter = postProcess(this.authFilter);
http.addFilter(filter);
}
/**
* <p>
* Specifies the URL to send users to if login is required. If used with
* {@link EnableWebSecurity} a default login page will be generated when this
* attribute is not specified.
* </p>
*
* <p>
* If a URL is specified or this is not being used in conjunction with
* {@link EnableWebSecurity}, users are required to process the specified URL to
* generate a login page.
* </p>
*/
protected T loginPage(String loginPage) {
setLoginPage(loginPage);
updateAuthenticationDefaults();
this.customLoginPage = true;
return getSelf();
}
/**
* @return true if a custom login page has been specified, else false
*/
public final boolean isCustomLoginPage() {
return this.customLoginPage;
}
/**
* Gets the Authentication Filter
* @return the Authentication Filter
*/
protected final F getAuthenticationFilter() {
return this.authFilter;
}
/**
* Sets the Authentication Filter
* @param authFilter the Authentication Filter
*/
protected final void setAuthenticationFilter(F authFilter) {
this.authFilter = authFilter;
}
/**
* Gets the login page
* @return the login page
*/
protected final String getLoginPage() {
return this.loginPage;
}
/**
* Gets the Authentication Entry Point
* @return the Authentication Entry Point
*/
protected final AuthenticationEntryPoint getAuthenticationEntryPoint() {
return this.authenticationEntryPoint;
}
/**
* Gets the URL to submit an authentication request to (i.e. where username/password
* must be submitted)
* @return the URL to submit an authentication request to
*/
protected final String getLoginProcessingUrl() {
return this.loginProcessingUrl;
}
/**
* Gets the URL to send users to if authentication fails
* @return the URL to send users if authentication fails (e.g. "/login?error").
*/
protected final String getFailureUrl() {
return this.failureUrl;
}
/**
* Updates the default values for authentication.
*/
protected final void updateAuthenticationDefaults() {
if (this.loginProcessingUrl == null) {
loginProcessingUrl(this.loginPage);
}
if (this.failureHandler == null) {
failureUrl(this.loginPage + "?error");
}
LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) {
logoutConfigurer.logoutSuccessUrl(this.loginPage + "?logout");
}
}
/**
* Updates the default values for access.
*/
protected final void updateAccessDefaults(B http) {
if (this.permitAll) {
PermitAllSupport.permitAll(http, this.loginPage, this.loginProcessingUrl, this.failureUrl);
}
}
/**
* Sets the loginPage and updates the {@link AuthenticationEntryPoint}.
* @param loginPage
*/
private void setLoginPage(String loginPage) {
this.loginPage = loginPage;
this.authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(loginPage);
}
@SuppressWarnings("unchecked")
private T getSelf() {
return (T) this;
}
}
AbstractAuthenticationFilterConfigurer
也是一个抽象类,继承了AbstractHttpConfigurer
,是模版模式,通过源码我们可知:
- 泛型
F
继承了AbstractAuthenticationProcessingFilter
抽象类,而AbstractAuthenticationProcessingFilter
继承了GenericFilterBean
,意味着F
是一个过滤器。
AbstractAuthenticationFilterConfigurer
作为配置器,我们只需抓住init
方法和configure
方法这两个核心即可:
查看init
方法:
@Override
public void init(B http) throws Exception {
updateAuthenticationDefaults();
updateAccessDefaults(http);
registerDefaultAuthenticationEntryPoint(http);
}
init
方法主要干了三件事:
updateAuthenticationDefaults
方法更新身份认证的默认值(包括:登录页面、失败处理、登出配置)。updateAccessDefaults
方法更新默认放行的访问控制。registerDefaultAuthenticationEntryPoint
方法注册默认身份认证端点(未认证处理)。
查看configure
方法:
@Override
public void configure(B http) throws Exception {
PortMapper portMapper = http.getSharedObject(PortMapper.class);
if (portMapper != null) {
this.authenticationEntryPoint.setPortMapper(portMapper);
}
RequestCache requestCache = http.getSharedObject(RequestCache.class);
if (requestCache != null) {
this.defaultSuccessHandler.setRequestCache(requestCache);
}
this.authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
this.authFilter.setAuthenticationSuccessHandler(this.successHandler);
this.authFilter.setAuthenticationFailureHandler(this.failureHandler);
if (this.authenticationDetailsSource != null) {
this.authFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
}
SessionAuthenticationStrategy sessionAuthenticationStrategy = http
.getSharedObject(SessionAuthenticationStrategy.class);
if (sessionAuthenticationStrategy != null) {
this.authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy);
}
RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class);
if (rememberMeServices != null) {
this.authFilter.setRememberMeServices(rememberMeServices);
}
SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
SecurityContextRepository securityContextRepository = securityContextConfigurer
.getSecurityContextRepository();
this.authFilter.setSecurityContextRepository(securityContextRepository);
}
this.authFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
F filter = postProcess(this.authFilter);
http.addFilter(filter);
}
configure
方法逻辑就比较简单了:构建各种各样的回调函数设置给 authFilter
,authFilter
再去 postProcess
中走一圈注册到Spring Ioc
容器中,最后再把authFilter
添加到过滤器链中。
authFilter
来自子类调用父类AbstractAuthenticationFilterConfigurer
的构造方法进行赋值。
4.2 FormLoginConfigurer
FormLoginConfigurer
在继承AbstractAuthenticationFilterConfigurer
时限定了父类泛型F
是 UsernamePasswordAuthenticationFilter
类型,也就意味着我们这里最终要配置的过滤是 UsernamePasswordAuthenticationFilter
。
FormLoginConfigurer
重写了init
方法,配置了一下默认的登录页面。其他的基本上都是从父类来的,未做太多改变。
源码注释:
添加基于表单的身份认证。 所有属性都有合理的默认值,使得所有参数都是可选的。 如果未指定loginPage(String)
,框架将生成默认登录页面。
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, FormLoginConfigurer<H>, UsernamePasswordAuthenticationFilter> {
/**
* Creates a new instance
* @see HttpSecurity#formLogin()
*/
public FormLoginConfigurer() {
super(new UsernamePasswordAuthenticationFilter(), null);
usernameParameter("username");
passwordParameter("password");
}
/**
* <p>
* Specifies the URL to send users to if login is required. If used with
* {@link EnableWebSecurity} a default login page will be generated when this
* attribute is not specified.
* </p>
*
* <p>
* If a URL is specified or this is not being used in conjunction with
* {@link EnableWebSecurity}, users are required to process the specified URL to
* generate a login page. In general, the login page should create a form that submits
* a request with the following requirements to work with
* {@link UsernamePasswordAuthenticationFilter}:
* </p>
*
* <ul>
* <li>It must be an HTTP POST</li>
* <li>It must be submitted to {@link #loginProcessingUrl(String)}</li>
* <li>It should include the username as an HTTP parameter by the name of
* {@link #usernameParameter(String)}</li>
* <li>It should include the password as an HTTP parameter by the name of
* {@link #passwordParameter(String)}</li>
* </ul>
*
* <h2>Example login.jsp</h2>
*
* Login pages can be rendered with any technology you choose so long as the rules
* above are followed. Below is an example login.jsp that can be used as a quick start
* when using JSP's or as a baseline to translate into another view technology.
*
* <pre>
* <!-- loginProcessingUrl should correspond to FormLoginConfigurer#loginProcessingUrl. Don't forget to perform a POST -->
* <c:url value="/login" var="loginProcessingUrl"/>
* <form action="${loginProcessingUrl}" method="post">
* <fieldset>
* <legend>Please Login</legend>
* <!-- use param.error assuming FormLoginConfigurer#failureUrl contains the query parameter error -->
* <c:if test="${param.error != null}">
* <div>
* Failed to login.
* <c:if test="${SPRING_SECURITY_LAST_EXCEPTION != null}">
* Reason: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
* </c:if>
* </div>
* </c:if>
* <!-- the configured LogoutConfigurer#logoutSuccessUrl is /login?logout and contains the query param logout -->
* <c:if test="${param.logout != null}">
* <div>
* You have been logged out.
* </div>
* </c:if>
* <p>
* <label for="username">Username</label>
* <input type="text" id="username" name="username"/>
* </p>
* <p>
* <label for="password">Password</label>
* <input type="password" id="password" name="password"/>
* </p>
* <!-- if using RememberMeConfigurer make sure remember-me matches RememberMeConfigurer#rememberMeParameter -->
* <p>
* <label for="remember-me">Remember Me?</label>
* <input type="checkbox" id="remember-me" name="remember-me"/>
* </p>
* <div>
* <button type="submit" class="btn">Log in</button>
* </div>
* </fieldset>
* </form>
* </pre>
*
* <h2>Impact on other defaults</h2>
*
* Updating this value, also impacts a number of other default values. For example,
* the following are the default values when only formLogin() was specified.
*
* <ul>
* <li>/login GET - the login form</li>
* <li>/login POST - process the credentials and if valid authenticate the user</li>
* <li>/login?error GET - redirect here for failed authentication attempts</li>
* <li>/login?logout GET - redirect here after successfully logging out</li>
* </ul>
*
* If "/authenticate" was passed to this method it update the defaults as shown below:
*
* <ul>
* <li>/authenticate GET - the login form</li>
* <li>/authenticate POST - process the credentials and if valid authenticate the user
* </li>
* <li>/authenticate?error GET - redirect here for failed authentication attempts</li>
* <li>/authenticate?logout GET - redirect here after successfully logging out</li>
* </ul>
* @param loginPage the login page to redirect to if authentication is required (i.e.
* "/login")
* @return the {@link FormLoginConfigurer} for additional customization
*/
@Override
public FormLoginConfigurer<H> loginPage(String loginPage) {
return super.loginPage(loginPage);
}
/**
* The HTTP parameter to look for the username when performing authentication. Default
* is "username".
* @param usernameParameter the HTTP parameter to look for the username when
* performing authentication
* @return the {@link FormLoginConfigurer} for additional customization
*/
public FormLoginConfigurer<H> usernameParameter(String usernameParameter) {
getAuthenticationFilter().setUsernameParameter(usernameParameter);
return this;
}
/**
* The HTTP parameter to look for the password when performing authentication. Default
* is "password".
* @param passwordParameter the HTTP parameter to look for the password when
* performing authentication
* @return the {@link FormLoginConfigurer} for additional customization
*/
public FormLoginConfigurer<H> passwordParameter(String passwordParameter) {
getAuthenticationFilter().setPasswordParameter(passwordParameter);
return this;
}
/**
* Forward Authentication Failure Handler
* @param forwardUrl the target URL in case of failure
* @return the {@link FormLoginConfigurer} for additional customization
*/
public FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {
failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));
return this;
}
/**
* Forward Authentication Success Handler
* @param forwardUrl the target URL in case of success
* @return the {@link FormLoginConfigurer} for additional customization
*/
public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
return this;
}
@Override
public void init(H http) throws Exception {
super.init(http);
initDefaultLoginFilter(http);
}
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return new AntPathRequestMatcher(loginProcessingUrl, "POST");
}
/**
* Gets the HTTP parameter that is used to submit the username.
* @return the HTTP parameter that is used to submit the username
*/
private String getUsernameParameter() {
return getAuthenticationFilter().getUsernameParameter();
}
/**
* Gets the HTTP parameter that is used to submit the password.
* @return the HTTP parameter that is used to submit the password
*/
private String getPasswordParameter() {
return getAuthenticationFilter().getPasswordParameter();
}
/**
* If available, initializes the {@link DefaultLoginPageGeneratingFilter} shared
* object.
* @param http the {@link HttpSecurityBuilder} to use
*/
private void initDefaultLoginFilter(H http) {
DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http
.getSharedObject(DefaultLoginPageGeneratingFilter.class);
if (loginPageGeneratingFilter != null && !isCustomLoginPage()) {
loginPageGeneratingFilter.setFormLoginEnabled(true);
loginPageGeneratingFilter.setUsernameParameter(getUsernameParameter());
loginPageGeneratingFilter.setPasswordParameter(getPasswordParameter());
loginPageGeneratingFilter.setLoginPageUrl(getLoginPage());
loginPageGeneratingFilter.setFailureUrl(getFailureUrl());
loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl());
}
}
}
另外我们日常配置的很多东西也是来自这里:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!