Spring Security 源码学习(三): Spring Security认证流程
【参考文章】: Spring Security 认证流程 (写的很形象)
认证功能由 springSecurityFilterChain 中的 UsernamePasswordAuthenticationFilter 实现
认证流程
- UsernamePasswordAuthenticationFilter 创建一个未认证的Authentication, 然后交给 AuthenticationManager 进行认证
- AuthenticationManager 的默认实现 ProviderManager 管理负责认证的AuthenticationProvider, 然后遍历AuthenticationProvider, 如果这个AuthenticationProvider 支持这种类型的认证, 将未认证信息交给 AuthenticationProvider 处理认证
- 认证成功则会返回一个通过认证的Authentication对象,否则抛异常表示认证失败
1. AbstractAuthenticationProcessingFilter(认证入口)
该类下有三个子类,子类都没有重写doFilter(),都是调用父类的doFilter()
- ClientCredentialsTokenEndpointFilter
- OAuth2ClientAuthenticationProcessingFilter(OAuth2下实现)
- UsernamePasswordAuthenticationFilter(默认实现)
doFilter()中调用了子类的attemptAuthentication()进行真正的认证逻辑处理
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
Authentication authResult;
try {
// 调用子类实现的抽象方法,返回一个验证对象
// 该方法的实现才是真正处理验证逻辑
authResult = attemptAuthentication(request, response);
if (authResult == null) {
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) {
// 验证失败
unsuccessfulAuthentication(request, response, failed);
return;
}
// 验证成功
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authResult);
}
}
2. UsernamePasswordAuthenticationFilter
- 根据用户名和密码生成一个 UsernamePasswordAuthenticationToken 类型的 Authentication
- 将 Authentication 交给 ProviderManager 处理
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
// 默认处理POST方法的 /login请求(这是默认的登录请求URI)
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
// 只处理POST方法的请求
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();
// 后续AuthenticationProvider 会根据 authRequest 的 class 类型判断自己是否进行认证
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
setDetails(request, authRequest);
// 此处 his.getAuthenticationManager() 返回的是 ProviderManager
return this.getAuthenticationManager().authenticate(authRequest);
}
}
3. ProviderManager
管理负责认证的 AuthenticationProvider
默认的 providers 只有一个,类型为 AnonymousAuthenticationProvider
默认的 parent 类型为 ProviderManager , 其 providers 为 DaoAuthenticationProvider, 默认情况下最终由 DaoAuthenticationProvider 处理认证
public class ProviderManager implements AuthenticationManager, MessageSourceAware,InitializingBean {
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);
// 有一个 AuthenticationProvider 认证通过则结束
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException e) {
prepareException(e, authentication);
throw e;
} catch (AuthenticationException e) {
lastException = e;
}
}
if (result == null && parent != null) {
try {
// 所有的provider都不支持校验, 则由parent进行校验
result = parentResult = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
}
catch (AuthenticationException e) {
lastException = parentException = e;
}
}
if (result != null) {
if (eraseCredentialsAfterAuthentication&& (result instanceof CredentialsContainer)) {
((CredentialsContainer) result).eraseCredentials();
}
if (parentResult == null) {
eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
...省略其他代码
}
}
4. DaoAuthenticationProvider
处理认证默认配置
- DaoAuthenticationProvider 的 authenticate() 继承自父类 AbstractUserDetailsAuthenticationProvider, 自己本身并未实现
- 父类中的 authenticate() 调用子类实现的 retrieveUser() 进行认证, 认证成功则返回一个 UserDetails 的实例, 否则认证失败
- DaoAuthenticationProvider 的 retrieveUser() 通过 UserDetailsService 的 loadUserByUsername() 方法获取 UserDetails 的实例
- UserDetailsService 就是我们一般实现的接口,并在 WebSecurityConfigurerAdapter 的实现类中进行配置
如果文章对您有所帮助,可以点一下推荐