【Spring Security】 登录核心类

Metadata

title: 【Spring Security】 登录核心类
date: 2023-02-02 11:51
tags:
  - 行动阶段/完成
  - 主题场景/组件
  - 笔记空间/KnowladgeSpace/ProgramSpace/ModuleSpace
  - 细化主题/Module/SpringSecurity
categories:
  - SpringSecurity
keywords:
  - SpringSecurity
description: 【Spring Security】 登录核心类

【Spring Security】 登录核心类

[[【Spring Security】 用户 密码 认证]]中我们分析了表单登录的基本流程,接下来分析下使用到的核心类。

UserDetails

UserDetails 翻译过来是用户详情的意思,它是一个接口,主要提供了保存用户信息的功能,登录时会查询数据库,封装 UserDetails 的子类实体,然后进行状态、密码等认证。

/**
 * 提供核心用户信息。出于安全目的,Spring Security 不直接使用实现。
 * 它们只是存储用户信息,然后将这些信息封装到 {@link Authentication} 对象中。
 * 这允许将非安全相关的用户信息(例如电子邮件地址、 电话号码等)存储在方便的位置。
 * 具体实现必须特别小心,以确保强制执行每个方法的详细非空契约。
 */
public interface UserDetails extends Serializable {

    /**
     * 返回授予用户的权限
     */
    Collection<? extends GrantedAuthority> getAuthorities();

    /**
     * 返回用于验证用户的密码
     */
    String getPassword();
    /**
     * 返回用于验证用户的用户名
     */
    String getUsername();
    /**
     * 指示用户的帐户是否已过期。过期的帐户无法进行身份验证
     */
    boolean isAccountNonExpired();
    /**
     * 指示用户是被锁定还是未锁定。 无法对锁定的用户进行身份验证
     */
    boolean isAccountNonLocked();
    /**
     * 指示用户的凭据(密码)是否已过期。过期的凭据会阻止身份验证
     */
    boolean isCredentialsNonExpired();
    /**
     * 指示用户是启用还是禁用。无法对禁用的用户进行身份验证
     */
    boolean isEnabled();

}

User

Security 中的 User 类,就是实际存储用户认证信息的模型了,它实现了 UserDetails 及 CredentialsContainer 接口,提供了有参构造,根据用户名、密码、权限集合、过期禁用等状态构造认证用户模型。还提供了 builder 构造模式来创建 User 类。我们可以直接使用此类或者继承它来实现用户信息封装。

1. 登录后如何获取 User 信息

可以从 SecurityContext 中获取

SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication = context.getAuthentication();
        User principal = (User)authentication.getPrincipal();

可以看出,登录后,对密码进行了设置 null 处理,User 中没有保存。

2. 扩展 User 信息

User 只包含了用户名、权限、状态等信息,某些情况下,我们需要其他额外信息,比如部门 ID、登录时间等,这时可以扩展这个 User 类。

  1. 自定义用户类,继承 User 类。
public class MyUser extends User {
    // 部门ID
    Integer deptId;
    // 登录时间
    Date LoginTime;

    // 调用父类构造
    public MyUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }

    public Integer getDeptId() {
        return deptId;
    }

    public void setDeptId(Integer deptId) {
        this.deptId = deptId;
    }

    public Date getLoginTime() {
        return LoginTime;
    }

    public void setLoginTime(Date loginTime) {
        LoginTime = loginTime;
    }
}
  1. UserDetailsService 返回自定义用户类,并设置扩展信息。
@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1. 数据库查询用户
        User user = userService.getUserByName(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在!");
        }
        // 2. 设置权限集合,后续需要数据库查询
        List<GrantedAuthority> authorityList =
                AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        // 3. 返回自定义的用户信息
        MyUser myUser = new MyUser(user.getUserName(), user.getPassword(), authorityList);
        // 4. 设置自定义扩展信息
        myUser.setDeptId(user.getOrganizationId());
        myUser.setLoginTime(new Date());
        return myUser;
    }

DaoAuthenticationProvider

DaoAuthenticationProvider 是一种 AuthenticationProvider,利用 UserDetailsService 和 PasswordEncoder 来验证用户名和密码的实现。

让我们来看看 DaoAuthenticationProvider 在 Spring Security 中是如何工作的。

  1. Filter 从读取用户名和密码的身份验证传递 UsernamePasswordAuthenticationToken 到由 AuthenticationManager 实现 ProviderManager。
  2. 在 ProviderManager 被配置为使用一个的 AuthenticationProvider 类型的 DaoAuthenticationProvider。
  3. DaoAuthenticationProvider 中查找 UserDetails 从 UserDetailsService。
  4. DaoAuthenticationProvider 然后使用 PasswordEncoder 验证 UserDetails 上一步返回的密码。
  5. 当身份验证成功时,Authentication 返回的 是类型 UsernamePasswordAuthenticationToken 并且具有 UserDetails 由配置的返回的主体 UserDetailsService。最终,返回的值 UsernamePasswordAuthenticationToken 将由 SecurityContextHolderauthentication 设置 Filter。

WebSecurityConfigurerAdapter 及 PasswordEncoder 后续专题介绍。