new無语 转载请注明原创出处,谢谢!
上章硬编码了一个user用户。记录一点基础的配置,这次结合高级认证灵活使用Spring Security的用户认证。
-
AuthenticationManager
: 身份验证的主要策略设置接口 -
ProviderManager
:AuthenticationManager
最常用的接口实现 -
AuthenticationProvider
:ProviderManager
的工作被委托者 -
Authentication
: 认证用户信息主体 -
GrantedAuthority
: 用户主体的权限 -
UserDetails
: 用户的基本必要信息 -
UserDetailsService
: 通过String username
返回一个UserDetails
-
SecurityContextHolder
: 提供访问SecurityContext
。 -
SecurityContext
: 保存Authentication
,和一些其它的信息
1. AuthenticationProvider
ProviderManager
把工作委托给AuthenticationProvider
集合。ProviderManager
将所有AuthenticationProvider
进行循环,直到运行返回一个完整的Authentication
,不符合条件或者不能认证当前Authentication
,返回AuthenticationException
异常或者null
。
@Component
public class SpringAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, "非验证类型");
//账号
String username = authentication.getName();
//密码
String password = authentication.getCredentials().toString();
List<SimpleGrantedAuthority> roles = new ArrayList<>();
if("testuser1".equalsIgnoreCase(username)){
roles.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}else if("testuser2".equalsIgnoreCase(username)){
roles.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
roles.add(new SimpleGrantedAuthority("ROLE_DBA"));
}else{
throw new UsernameNotFoundException("用户名/密码无效");
}
return new UsernamePasswordAuthenticationToken(username, password, roles);
}
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
此情况在AuthenticationManagerBuilder
没有配置的情况下生效。(要把配置文件配置的用户账号/密码/角色配置信息注释掉)
Collection<? extends GrantedAuthority>
是Authentication
的角色信息。在认证成功返回时添加到Authentication
中。
supports(Class<?> authentication)
方法是判断是否支持当前Authentication
类型的认证。(JaasAuthenticationToken
、OAuth2Authentication
、UsernamePasswordAuthenticationToken
、自己实现)等。
上面定义了两个用户,分别是testuser1
和testuser2
,分别赋权ADMIN
和ROLE_ADMIN
+ROLE_DBA
(符合上章的权限请求路径)(Spring Security拦截器角色验证前缀"ROLE_")。
运行项目进行测试角色是否正确。http://localhost:8080/login
进行登录,分别访问http://localhost:8080/db/123
或http://localhost:8080/admin/123
返回404(有权限访问)或者403(无权限访问)。
2. UserDetailsService
实现UserDetailsService
方法,提供UserDetails
。
我们把上面的用户登录的查询功能挪动到UserDetailsSerivce
接口实现中。
@Service
public class SpringUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails userDetails = null;
List<GrantedAuthority> roles = new ArrayList<>();
if ("testuser1".equalsIgnoreCase(username)) {
roles.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
} else if ("testuser2".equalsIgnoreCase(username)) {
roles.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
roles.add(new SimpleGrantedAuthority("ROLE_DBA"));
} else {
return null;
}
userDetails = new User(username, "password", roles);
return userDetails;
}
}
之后再对认证代码进行改动。
@Component
public class SpringAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, "非验证类型");
//账号
String username = authentication.getName();
//密码
String password = authentication.getCredentials().toString();
List<SimpleGrantedAuthority> roles = new ArrayList<>();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails == null) {
throw new UsernameNotFoundException("用户名/密码无效");
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password, roles);
token.setDetails(userDetails);
return token;
}
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
3. PasswordEncoder
Spring Security提供了很多加密方式,MD5、SHA、BCrypt等,官方推荐使用BCrypt,好处就不在这说了。自己百度下吧。提供的支持类就是BCryptPasswordEncoder
。使用方式简单,直接初始化。注入就可以用了。两个方法加密、解密。
PasswordEncoder
/**
* Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or
* greater hash combined with an 8-byte or greater randomly generated salt.
*/
String encode(CharSequence rawPassword);
/**
* Verify the encoded password obtained from storage matches the submitted raw
* password after it too is encoded. Returns true if the passwords match, false if
* they do not. The stored password itself is never decoded.
*
* @param rawPassword the raw password to encode and match
* @param encodedPassword the encoded password from storage to compare with
* @return true if the raw password, after encoding, matches the encoded password from
* storage
*/
boolean matches(CharSequence rawPassword, String encodedPassword);
网友评论