最近用SpringSecurity结合Jwt、在此记录一下SpringSecurity的登录授权过程、(只是略懂了冰山一角、还有很多配置都未涉及、现在了解的只是最常见最常用的配置)、SpringSecurity的验证方式有很多、不过大部分使用的还是UserDetailsService
的方式
1、UsernamePasswordAuthenticationFilter
位于Security
的Security Filter Chain
链的第4位、该类主要作用身份验证处理机制-用户名密码身份验证筛选器-以便可以修改SecurityContextHolder以包含有效的身份验证请求令牌
、该类继承AbstractAuthenticationProcessingFilter
、在AbstractAuthenticationProcessingFilter
的doFilter中代码如下
3、UsernamePasswordAuthenticationFilter
会对其进行判断、抽取username
和password
以及封装成身份验证令牌
4、ProviderManager
调用authenticate
方法、进行身份验证
5、ProviderManager
获取所拥有的AuthenticationProvider
进行验证、一般我们使用的是DaoAuthenticationProvider
验证类、该类继承 AbstractUserDetailsAuthenticationProvider
类、在AbstractUserDetailsAuthenticationProvider
的authenticate
方法中封装了具体的验证过程、具体如下
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {
return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");
});
String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);//获取缓存的
if (user == null) { //如过user为空则调用DaoAuthenticationProvider的retrieveUser方法获取用户信息
cacheWasUsed = false;
try {
//获取用户信息、一般是调用自定义的 UserDetailsService
user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
} catch (UsernameNotFoundException var6) {
this.logger.debug("User '" + username + "' not found");
if (this.hideUserNotFoundExceptions) {
throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
throw var6;
}
Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
}
try {
//UserDetails接口下有几个返回 boolean的方法是在这里效验的、例如:账户停用等
this.preAuthenticationChecks.check(user);
//这个是验证密码是否正确的、和`retrieveUser`方法一样都是调用DaoAuthenticationProvider去处理
this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
} catch (AuthenticationException var7) {
if (!cacheWasUsed) {
throw var7;
}
cacheWasUsed = false;
user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
this.preAuthenticationChecks.check(user);
this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
}
this.postAuthenticationChecks.check(user);
if (!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
Object principalToReturn = user;
if (this.forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
return this.createSuccessAuthentication(principalToReturn, authentication, user);
}
6、处理流程图如下
等有时间在写
登录成功处理
和登录失败处理
流程、以及匿名访问受保护资源异常
和认证过的用户访问无权限资源的异常
的自定义处理方式、另附上官方文档SpringSecurity文档