概述
介绍
JdbcUserDetailsManagerConfigurer
是Spring Security Config
提供的一个安全配置器SecurityConfigurer
,用来配置一个安全构建器ProviderManagerBuilder
(通常可以认为就是AuthenticationManagerBuilder
),它为目标安全构建器提供的是一个基于关系型数据库的用户账号详情管理对象DaoAuthenticationProvider
。
具体来讲,JdbcUserDetailsManagerConfigurer
实现了接口SecurityConfigurer
,它的主要配置动作是:
- 创建一个
JdbcUserDetailsManager
(UserDetailsManager/UserDetailsService
的一个实现类); - 创建一个
DaoAuthenticationProvider
,将上面所创建的JdbcUserDetailsManager
作为自己的UserDetailsService userDetailsService
属性; - 将上面所创建的
DaoAuthenticationProvider
添加到目标构建器ProviderManagerBuilder
上。
除了以上主要的配置能力,JdbcUserDetailsManagerConfigurer
提供了其他一些辅助能力:
1. 指定所要使用的数据源;
2. 是否使用缺省脚本对数据源进行初始化;
3. 如果是用的不是缺省表结构,允许指定相应的查询语句;
4. 指定所要使用的权限前缀,缺省值为"";
另外,因为JdbcUserDetailsManagerConfigurer
继承自UserDetailsManagerConfigurer
,所以UserDetailsManagerConfigurer
所具备的能力,JdbcUserDetailsManagerConfigurer
都拥有。
继承关系
使用
// 通常开发人员的WebSecurityConfigurerAdapter实现类在方法
// public void configure(AuthenticationManagerBuilder builder) throws Exception 中有如下调用 :
// 这里 builder 是 AuthenticationManagerBuilder
builder.jdbcAuthentication().dataSource(dataSource).passwordEncoder(NoOpPasswordEncoder.getInstance())
.usersByUsernameQuery("select username,password, enabled from users where username=?")
.authoritiesByUsernameQuery("select username, role from user_roles where username=?");
源代码
源代码版本 Spring Security Config 5.1.4.RELEASE
package org.springframework.security.config.annotation.authentication.configurers.provisioning;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
import org.springframework.jdbc.datasource.init.DatabasePopulator;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
/**
* Configures an
* 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.
*
*
* The only required method is the #dataSource(javax.sql.DataSource) all other
* methods have reasonable defaults.
*
* @param <B> the type of the ProviderManagerBuilder that is being configured
*
* @author Rob Winch
* @since 3.2
*/
public class JdbcUserDetailsManagerConfigurer<B extends ProviderManagerBuilder<B>>
extends UserDetailsManagerConfigurer<B, JdbcUserDetailsManagerConfigurer<B>> {
// 创建目标 JdbcUserDetailsManager 所要使用的数据源
private DataSource dataSource;
// 如果要在数据源中初始化所需表格,这里是初始化脚本
private List<Resource> initScripts = new ArrayList<>();
// 构造函数,使用外部指定的 JdbcUserDetailsManager 对象
public JdbcUserDetailsManagerConfigurer(JdbcUserDetailsManager manager) {
super(manager);
}
// 构造函数,使用内部新建的 JdbcUserDetailsManager 对象
public JdbcUserDetailsManagerConfigurer() {
this(new JdbcUserDetailsManager());
}
/**
* Populates the DataSource to be used. This is the only required attribute.
*
* 使用外部指定的数据源,改参数不能是 null
* 该数据源会被用于当前对象 userDetailsService 属性
* (也就是上面构造函数里面提到的JdbcUserDetailsManager实例)的数据源
* @param dataSource the DataSource to be used. Cannot be null.
* @return The JdbcUserDetailsManagerConfigurer used for additional customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> dataSource(DataSource dataSource)
throws Exception {
this.dataSource = dataSource;
getUserDetailsService().setDataSource(dataSource);
return this;
}
/**
* 设置根据用户名查询用户账号信息的SQL语句。用在不使用缺省表格结构的情况。
* Sets the query to be used for finding a user by their username. For example:
*
*
* select username,password,enabled from users where username = ?
*
* @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 JdbcUserDetailsManagerConfigurer used for additional
* customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> usersByUsernameQuery(String query)
throws Exception {
getUserDetailsService().setUsersByUsernameQuery(query);
return this;
}
/**
* 设置根据用户名查询用户权限的SQL语句。用在不使用缺省表格结构的情况。
* Sets the query to be used for finding a user's authorities by their username. For
* example:
*
*
* select username,authority from authorities where username = ?
*
*
* @param query The query to use for selecting the username, authority by username.
* Must contain a single parameter for the username.
* @return The JdbcUserDetailsManagerConfigurer used for additional
* customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> authoritiesByUsernameQuery(String query)
throws Exception {
getUserDetailsService().setAuthoritiesByUsernameQuery(query);
return this;
}
/**
* 设置查询用户所属组的权限的SQL语句。用在不使用缺省表格结构并启用了用户组功能的情况。
* An SQL statement to query user's group authorities given a username. For example:
*
*
* 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
*
*
* @param query The query to use for selecting the authorities by group. Must contain
* a single parameter for the username.
* @return The JdbcUserDetailsManagerConfigurer used for additional
* customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> groupAuthoritiesByUsername(String query)
throws Exception {
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
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> rolePrefix(String rolePrefix)
throws Exception {
getUserDetailsService().setRolePrefix(rolePrefix);
return this;
}
/**
* Defines the UserCache to use
* 定义所使用的用户缓存对象 UserCache 到 userDetailsService JdbcUserDetailsManager
* @param userCache the UserCache to use
* @return the JdbcUserDetailsManagerConfigurer for further customizations
* @throws Exception
*/
public JdbcUserDetailsManagerConfigurer<B> userCache(UserCache userCache)
throws Exception {
getUserDetailsService().setUserCache(userCache);
return this;
}
// 初始化 userDetailsService JdbcUserDetailsManager :
// 1. 如果初始化脚本存在,则使用初始化脚本初始化数据源(创建相应表格结构);
// 2. 如果外部指定了用户账号信息,将这些用户账号信息添加到 userDetailsService
// JdbcUserDetailsManager,从而使它们处于管理之中。
@Override
protected void initUserDetailsService() throws Exception {
if (!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.
* 获取缺省的数据源初始化脚本,注意该内置脚本仅仅针对 H2 数据库,对于 MySQL, Oracle
* 此脚本并不适用
* @return The 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(initScripts.toArray(new Resource[initScripts.size()]));
return dbp;
}
// 获取数据源初始化器对象,该对象基于上面提到的数据库填充器对象和所设置的数据源
private DataSourceInitializer getDataSourceInit() {
DataSourceInitializer dsi = new DataSourceInitializer();
dsi.setDatabasePopulator(getDatabasePopulator());
dsi.setDataSource(dataSource);
return dsi;
}
}