Spring Security 6.x 系列(10)—— SecurityConfigurer 配置器及其分支实现源码分析(二)

2023-12-13 03:53:42

一、前言

在本系列文章:

Spring Security 6.x 系列(4)—— 基于过滤器链的源码分析(一)
中着重分析了Spring SecuritySpring Boot自动配置、 DefaultSecurityFilterChainFilterChainProxy 的构造过程。

Spring Security 6.x 系列(7)—— SecurityBuilder 继承链源码分析
中详细分析了Spring SecurityWebSecurityHttpSecurityAuthenticationManagerBuilder 三个构造器的公共继承链。

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子类:

在这里插入图片描述
过滤器的配置都在这里面,我们可通过修改配置来实现自定义业务扩展。

下文我们着重分析下AbstractAuthenticationFilterConfigurerFormLoginConfigurer,其他子类实现在后续的业务实战章节我们再逐个进行分析。

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方法逻辑就比较简单了:构建各种各样的回调函数设置给 authFilterauthFilter再去 postProcess中走一圈注册到Spring Ioc容器中,最后再把authFilter添加到过滤器链中。

authFilter来自子类调用父类AbstractAuthenticationFilterConfigurer的构造方法进行赋值。

4.2 FormLoginConfigurer

FormLoginConfigurer在继承AbstractAuthenticationFilterConfigurer时限定了父类泛型FUsernamePasswordAuthenticationFilter类型,也就意味着我们这里最终要配置的过滤是 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 -->
	 * &lt;c:url value="/login" var="loginProcessingUrl"/&gt;
	 * &lt;form action="${loginProcessingUrl}" method="post"&gt;
	 *    &lt;fieldset&gt;
	 *        &lt;legend&gt;Please Login&lt;/legend&gt;
	 *        &lt;!-- use param.error assuming FormLoginConfigurer#failureUrl contains the query parameter error --&gt;
	 *        &lt;c:if test="${param.error != null}"&gt;
	 *            &lt;div&gt;
	 *                Failed to login.
	 *                &lt;c:if test="${SPRING_SECURITY_LAST_EXCEPTION != null}"&gt;
	 *                  Reason: &lt;c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" /&gt;
	 *                &lt;/c:if&gt;
	 *            &lt;/div&gt;
	 *        &lt;/c:if&gt;
	 *        &lt;!-- the configured LogoutConfigurer#logoutSuccessUrl is /login?logout and contains the query param logout --&gt;
	 *        &lt;c:if test="${param.logout != null}"&gt;
	 *            &lt;div&gt;
	 *                You have been logged out.
	 *            &lt;/div&gt;
	 *        &lt;/c:if&gt;
	 *        &lt;p&gt;
	 *        &lt;label for="username"&gt;Username&lt;/label&gt;
	 *        &lt;input type="text" id="username" name="username"/&gt;
	 *        &lt;/p&gt;
	 *        &lt;p&gt;
	 *        &lt;label for="password"&gt;Password&lt;/label&gt;
	 *        &lt;input type="password" id="password" name="password"/&gt;
	 *        &lt;/p&gt;
	 *        &lt;!-- if using RememberMeConfigurer make sure remember-me matches RememberMeConfigurer#rememberMeParameter --&gt;
	 *        &lt;p&gt;
	 *        &lt;label for="remember-me"&gt;Remember Me?&lt;/label&gt;
	 *        &lt;input type="checkbox" id="remember-me" name="remember-me"/&gt;
	 *        &lt;/p&gt;
	 *        &lt;div&gt;
	 *            &lt;button type="submit" class="btn"&gt;Log in&lt;/button&gt;
	 *        &lt;/div&gt;
	 *    &lt;/fieldset&gt;
	 * &lt;/form&gt;
	 * </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());
		}
	}

}

另外我们日常配置的很多东西也是来自这里:

在这里插入图片描述

文章来源:https://blog.csdn.net/ctwy291314/article/details/134880152
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。