SpringSecurity基本原理是基于一个过滤器链
我们启动一个SpringSecurity项目,日志会打印过滤器
一共有十二个过滤器,大致介绍一下
最常用的就是UsernamePasswordAuthenticationFilter 了
接下来我们就对UsernamePasswordAuthenticationFilter进行一个源码分析
核心方法:attemptAuthentication做了以下几件事情
public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
然而AuthenticationManager是一个接口,对应的实现类是ProviderManager,查看以下authenticate的实现方法
ProviderManager 中的List,会依照次序去认证,认证成功则立即返回,若认证失败则返回null,下一个AuthenticationProvider会继续尝试认证,如果所有认证器都无法认证成功,则ProviderManager
会抛出一个ProviderNotFoundException异常。
public Authentication authenticate(Authentication authentication)throws AuthenticationException {
......
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
}
}
所以实现校验的过程的是AuthenticationProvider的实现类,比如使用表单验证的情况下,默认是使用DaoAuthenticationProvider
DaoAuthenticationProvider继承了AbstractUserDetailsAuthenticationProvider,authenticate方法aut是在AbstractUserDetailsAuthenticationProvider
authenticate方法做了什么呢?
//1.获取UserDetails
UserDetails user = this.userCache.getUserFromCache(username);
//2.通过UserDetailsService去获取UserDeatails对象
user = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);
//3.预检查,对UserDetails进行提前的检查,判断是否锁定,是否过期,是否可用
preAuthenticationChecks.check(user);
//4.附加检查,判断是否有证书和是否有用passwordEncoder加密
additionalAuthenticationChecks(user,(UsernamePasswordAuthenticationToken) authentication);
//5.最后检查,判断证书是否过期
postAuthenticationChecks.check(user);
//6.返回认证成功的信息
return createSuccessAuthentication(principalToReturn, authentication, user);
reateSuccessAuthentication(principalToReturn, authentication, user)源码:
重写组装了一个UsernamePasswordAuthenticationToken,和第一次的组装不一样的地方在于
添加了权限和设置了已验证的信息
//第一次的组装
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
//第二次的组装
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true); // must use super, as we override
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
// Ensure we return the original credentials the user supplied,
// so subsequent attempts are successful even with encoded passwords.
// Also ensure we return the original getDetails(), so that future
// authentication events after cache expiry contain the details
UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
principal, authentication.getCredentials(),
authoritiesMapper.mapAuthorities(user.getAuthorities()));
result.setDetails(authentication.getDetails());
return result;
}
整个过程 一旦任何一处发送异常,都会被捕获AbstractAuthenticationProcessingFilter类里面捕获
try {
//这里是UsernamePasswordAuthenticationFilter的最后返回值
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
//调用登陆成功的处理器
successfulAuthentication(request, response, chain, authResult);