Spring Security Config : JdbcUserDetailsManagerConfigurer

JdbcUserDetailsManagerConfigurer是Spring Security配置组件,用于基于关系型数据库配置用户账号详情管理。它创建JdbcUserDetailsManager,配置DaoAuthenticationProvider,并添加至ProviderManagerBuilder。支持自定义数据源、SQL查询、权限前缀等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

介绍

JdbcUserDetailsManagerConfigurerSpring Security Config提供的一个安全配置器SecurityConfigurer,用来配置一个安全构建器ProviderManagerBuilder(通常可以认为就是AuthenticationManagerBuilder),它为目标安全构建器提供的是一个基于关系型数据库的用户账号详情管理对象DaoAuthenticationProvider

具体来讲,JdbcUserDetailsManagerConfigurer实现了接口SecurityConfigurer,它的主要配置动作是:

  1. 创建一个JdbcUserDetailsManager(UserDetailsManager/UserDetailsService的一个实现类);
  2. 创建一个DaoAuthenticationProvider,将上面所创建的JdbcUserDetailsManager作为自己的UserDetailsService userDetailsService属性;
  3. 将上面所创建的DaoAuthenticationProvider添加到目标构建器ProviderManagerBuilder上。

除了以上主要的配置能力,JdbcUserDetailsManagerConfigurer提供了其他一些辅助能力:
1. 指定所要使用的数据源;
2. 是否使用缺省脚本对数据源进行初始化;
3. 如果是用的不是缺省表结构,允许指定相应的查询语句;
4. 指定所要使用的权限前缀,缺省值为"";

另外,因为JdbcUserDetailsManagerConfigurer继承自UserDetailsManagerConfigurer,所以UserDetailsManagerConfigurer所具备的能力,JdbcUserDetailsManagerConfigurer都拥有。

继承关系

UserDetailsManagerConfigurer

使用


 // 通常开发人员的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;
	}
}

参考文章

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值