跳至主要內容

UserDetailsManager 入门

Jin大约 5 分钟

UserDetailsManager 入门

1、继承关系图

![UserDetailsService](images/10-UserDetailsManager 入门/UserDetailsService.png)

2、简介

UserDetailsManager 是一个接口,继承自 UserDetailsService,主要用于管理用户的创建、更新、删除等操作。它通常在处理用户的账户信息时比 UserDetailsService 提供了更多的功能。UserDetailsService 主要用于根据用户名加载用户信息,而 UserDetailsManager 则扩展了这些功能,允许对用户信息进行增删改操作。

3、接口

接口源码

public interface UserDetailsManager extends UserDetailsService {
// 创建一个新用户。参数是一个 UserDetails 实例,它通常包含用户名、密码和角色等信息。
	void createUser(UserDetails user);
// 更新已有用户的信息。和 createUser 方法类似,传入一个 UserDetails 实例,但是它是用来更新已经存在的用户信息。
	void updateUser(UserDetails user);
// 根据用户名删除用户。传入用户名来删除对应的用户账户。
	void deleteUser(String username);
// 修改密码。该方法用于修改当前用户的密码。通常,修改密码时会需要验证原密码。
	void changePassword(String oldPassword, String newPassword);
// 检查用户是否存在。该方法会根据用户名判断用户是否存在。
	boolean userExists(String username);
}

4、实现

4.1、自定义管理器 (推荐)

自定义一个 UserDetailsManager 实现可以让你更灵活地控制用户的管理流程。通过继承和实现 Spring Security 提供的接口,你可以根据实际需求将用户信息存储到不同的数据源或使用不同的存储方案(如内存、数据库、分布式缓存等)。

  • 优势:这种方法非常灵活,适合在需要特定用户管理逻辑的场景下使用。
  • 注意事项:确保密码加密、用户验证逻辑的安全性,并在实际生产环境中使用合适的存储方式(例如数据库、NoSQL 数据库等)。

4.2、InMemoryUserDetailsManager

InMemoryUserDetailsManager 是 Spring Security 中的一个内存实现的 UserDetailsManager,它用来存储用户信息并管理用户的认证和授权。它主要适用于开发阶段或轻量级的应用场景,其中用户数据存储在内存中,而不是持久化到数据库。

主要特性

  1. 内存存储用户信息:用户信息以 UserDetails 的形式保存在内存中。适用于不需要持久化存储的简单应用。
  2. 用户管理功能:它支持用户的创建、删除、更新和检查是否存在等操作。
  3. 简单易用:非常适合在没有外部数据库或在开发阶段快速原型制作时使用。

适用场景

InMemoryUserDetailsManager 适合用于以下场景:

  • 开发和测试环境:开发者需要快速验证应用的安全配置或做原型设计时,使用内存存储的用户信息可以避免配置复杂的数据库。
  • 轻量级应用:对于那些用户量不大且无需持久化存储的应用,可以使用它来简化开发工作。
  • 原型设计:在系统的早期阶段,当用户认证功能尚不完全确定时,可以使用它作为临时解决方案。

注意事项

  • 不适合生产环境:由于 InMemoryUserDetailsManager 只会在应用运行时保留用户信息,当应用重启时,所有数据都会丢失。因此,它不适合用在需要持久化用户信息的生产环境中。
  • 密码存储:为了安全起见,应该始终使用密码加密。在示例中,{noop} 表示不加密密码,但在实际使用中,你应该使用如 {bcrypt}{pbkdf2} 等加密方式来存储密码。

JdbcUserDetailsManager 是 Spring Security 提供的一种用于与数据库交互的 UserDetailsManager 实现,它允许应用通过数据库存储和管理用户信息。与 InMemoryUserDetailsManager 不同,JdbcUserDetailsManager 将用户数据持久化到数据库中,通常用于生产环境中处理较大用户量的场景。

4.3、JdbcUserDetailsManager

主要功能

JdbcUserDetailsManager 继承自 UserDetailsManager 接口,提供了以下主要功能:

  • 用户创建:通过数据库创建新的用户。
  • 用户更新:更新数据库中已有用户的相关信息。
  • 用户删除:根据用户名删除数据库中的用户信息。
  • 检查用户存在性:检查数据库中是否存在某个特定用户名的用户。
  • 密码修改:修改数据库中当前用户的密码。

适用场景

  • 生产环境:当应用需要存储大量用户数据,并且希望能将用户信息持久化到数据库中时,JdbcUserDetailsManager 是一个合适的选择。
  • 多种存储方案:它支持多种数据库存储模式,可以自定义查询语句来适应不同的数据库设计。

配置示例

在使用 JdbcUserDetailsManager 时,首先需要确保你的数据库表结构符合 Spring Security 的默认要求。然后,你需要配置一个 DataSource 来让 JdbcUserDetailsManager 与数据库进行交互。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsManager;
import org.springframework.security.core.userdetails.JdbcUserDetailsManager;
import org.springframework.security.core.userdetails.UserDetails;
import javax.sql.DataSource;

@Configuration
public class SecurityConfig {

    // 配置 HTTP 安全性
    @Bean
    public UserDetailsManager userDetailsManager(DataSource dataSource) {
        JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
        manager.setDataSource(dataSource);  // 设置数据源
        return manager;
    }

    @Bean
    public DataSource dataSource() {
        // 配置数据源(例如:H2、MySQL、PostgreSQL等)
        // 这里以 H2 数据库为例
        org.springframework.jdbc.datasource.DriverManagerDataSource dataSource = new org.springframework.jdbc.datasource.DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/security-demo);
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    // 配置 HTTP 安全性
    @Bean
    public HttpSecurity security(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .anyRequest().authenticated()
            .and()
            .formLogin();
        return http;
    }
}

贡献者: Jin