Spring Security 源码学习(三): Spring Security认证流程
2022/5/26 1:51:16
本文主要是介绍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 的实现类中进行配置
这篇关于Spring Security 源码学习(三): Spring Security认证流程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南