UserDetailService只负责用户名检索用户,UserDetailsManager添加了,修改或删除用户的行为,
GrantedAuthority代表用户所拥有的一个或多个权限,UserDetail则代表契约用户

UserDetails

image-1652099094084
getUsername()和getPassword()会返回用户的密码,getauthorties()则可以返回用户的权限组
其余四个可以让用户账户

  1. 使账户过期
  2. 锁定账户
  3. 使凭据过期
  4. 禁用账户

GrandAuthority

image-1652099292315
只有一个方法用来获取用户所拥有的权限
GrantedAuthority g1 = () -> “read”;
GrantedAuthority g2=new SimpleGrantedAuthority(“read”);

UserDetails 的实现

通过实现UserDetails接口来自定义一个用户类

public class DummyUser implements UserDetails {

  /**
   * Returns the authorities granted to the user. Cannot return <code>null</code>.
   *
   * @return the authorities, sorted by natural key (never <code>null</code>)
   */
  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return List.of(()->"read");
  }

  /**
   * Returns the password used to authenticate the user.
   *
   * @return the password
   */
  @Override
  public String getPassword() {
    return "12345";
  }

  /**
   * Returns the username used to authenticate the user. Cannot return <code>null</code>.
   *
   * @return the username (never <code>null</code>)
   */
  @Override
  public String getUsername() {
    return "bill";
  }

  /**
   * Indicates whether the user's account has expired. An expired account cannot be authenticated.
   *
   * @return <code>true</code> if the user's account is valid (ie non-expired), <code>false</code>
   *     if no longer valid (ie expired)
   */
  @Override
  public boolean isAccountNonExpired() {
    return true;
  }

  /**
   * Indicates whether the user is locked or unlocked. A locked user cannot be authenticated.
   *
   * @return <code>true</code> if the user is not locked, <code>false</code> otherwise
   */
  @Override
  public boolean isAccountNonLocked() {
    return true;
  }

  /**
   * Indicates whether the user's credentials (password) has expired. Expired credentials prevent
   * authentication.
   *
   * @return <code>true</code> if the user's credentials are valid (ie non-expired), <code>false
   *     </code> if no longer valid (ie expired)
   */
  @Override
  public boolean isCredentialsNonExpired() {
    return true;
  }

  /**
   * Indicates whether the user is enabled or disabled. A disabled user cannot be authenticated.
   *
   * @return <code>true</code> if the user is enabled, <code>false</code> otherwise
   */
  @Override
  public boolean isEnabled() {
    return true;
  }
}

User

User是Spring Security提供的一种UserDetails的实现

    User.withUsername("bill")
        .password("12345")
        .authorities("read", "write")
        .accountExpired(false)
        .disabled(true)
        .build();

自定义实现一个UserDetailsService

我们先定义一个继承UserDetail的用户,用来存储用户密码 用户名 权限

public class User implements UserDetails {
private final String username;
private final String password;
private final String authority;

  public User(String username, String password, String authority) {
    this.username = username;
    this.password = password;
    this.authority = authority;
  }

  /**
   * Returns the authorities granted to the user. Cannot return <code>null</code>.
   *
   * @return the authorities, sorted by natural key (never <code>null</code>)
   */
  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return List.of(() -> authority);
  }

  /**
   * Returns the password used to authenticate the user.
   *
   * @return the password
   */
  @Override
  public String getPassword() {
    return password;
  }

  /**
   * Returns the username used to authenticate the user. Cannot return
   * <code>null</code>.
   *
   * @return the username (never <code>null</code>)
   */
  @Override
  public String getUsername() {
    return username;
  }

  /**
   * Indicates whether the user's account has expired. An expired account cannot be authenticated.
   *
   * @return <code>true</code> if the user's account is valid (ie non-expired),
   * <code>false</code> if no longer valid (ie expired)
   */
  @Override
  public boolean isAccountNonExpired() {
    return true;
  }

  /**
   * Indicates whether the user is locked or unlocked. A locked user cannot be authenticated.
   *
   * @return <code>true</code> if the user is not locked, <code>false</code> otherwise
   */
  @Override
  public boolean isAccountNonLocked() {
    return true;
  }

  /**
   * Indicates whether the user's credentials (password) has expired. Expired credentials prevent
   * authentication.
   *
   * @return <code>true</code> if the user's credentials are valid (ie non-expired),
   * <code>false</code> if no longer valid (ie expired)
   */
  @Override
  public boolean isCredentialsNonExpired() {
    return true;
  }

  /**
   * Indicates whether the user is enabled or disabled. A disabled user cannot be authenticated.
   *
   * @return <code>true</code> if the user is enabled, <code>false</code> otherwise
   */
  @Override
  public boolean isEnabled() {
    return true;
  }
}

然后模仿内存内管理器定义一个UserDetailsService 用来通过用户名查找用户

public class InMemoryUserDetailsService implements UserDetailsService {
  private final List<User> users;

  public InMemoryUserDetailsService(List<User> users) {
    this.users = users;
  }

  /**
   * Locates the user based on the username. In the actual implementation, the search may possibly
   * be case sensitive, or case insensitive depending on how the implementation instance is
   * configured. In this case, the <code>UserDetails</code> object that comes back may have a
   * username that is of a different case than what was actually requested..
   *
   * @param username the username identifying the user whose data is required.
   * @return a fully populated user record (never <code>null</code>)
   * @throws UsernameNotFoundException if the user could not be found or the user has no
   *     GrantedAuthority
   */
  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    return users.stream()
        .filter(user -> user.getUsername().equals(username))
        .findFirst()
        .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
  }
}

最后使用配置类对自定义的上述类进行配置

@Configuration
public class ProjectConfig {
  @Bean
  public UserDetailsService userDetailsService() {
    var user = new User("john", "12345", "read");
    var users = List.of(user);
    return new InMemoryUserDetailsService(users);
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return NoOpPasswordEncoder.getInstance();
  }
}

使用不同的UserDetailsManager来载入用户

image-1652102507477
这里可以看出UserDetailService是UserDetailManager结合一开始的声明,可知UserDetailManager扩展了UserDetailService
这里我们使用JDBCUserDetailManager来进行用户管理

@Configuration
public class ProjectConfig {
  @Bean
  public UserDetailsService userDetailsService(DataSource dataSource) {
    String usersByUsernameQuery =
        "select username, password, enabled from users where username = ?";
    String authoritiesByUsernameQuery =
        "select username, authority from authorities where username = ?";

    var jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);
    jdbcUserDetailsManager.setUsersByUsernameQuery(usersByUsernameQuery);
    jdbcUserDetailsManager.setAuthoritiesByUsernameQuery(authoritiesByUsernameQuery);
    return jdbcUserDetailsManager;
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return NoOpPasswordEncoder.getInstance();
  }
}

数据库存储的是John 12345,定义两张数据表就能获得授权信息,但这种依旧不够灵活,后续会缓冲MyBatis 或者JPA

Q.E.D.