UserDetailService只负责用户名检索用户,UserDetailsManager添加了,修改或删除用户的行为,
GrantedAuthority代表用户所拥有的一个或多个权限,UserDetail则代表契约用户
UserDetails
getUsername()和getPassword()会返回用户的密码,getauthorties()则可以返回用户的权限组
其余四个可以让用户账户
- 使账户过期
- 锁定账户
- 使凭据过期
- 禁用账户
GrandAuthority
只有一个方法用来获取用户所拥有的权限
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来载入用户
这里可以看出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.