SpringSecurity认证专题之【AuthenticationManager】

た 入场券 2022-12-14 13:39 417阅读 0赞

  哈喽,大家好,最近有段时间没有写博客了,今天开始我会陆续给大家整理出SpringSecurity原理源码相关的文件,本篇文章主要是给大家介绍下认证体系中最基础的AuthenticationManager的内容,让你对它从整体上面有一个认知。

AuthenticationManager

  首先我们来看下AuthenticationManager这个接口的定义。

  1. public interface AuthenticationManager {
  2. /** * 定义的一个认证的方法 **/
  3. Authentication authenticate(Authentication authentication)
  4. throws AuthenticationException;
  5. }

  通过源码能发现,单纯的就是定义了一个认证的方法,所以要分析的话我们要看下他的实现,在SpringSecurity中默认的AuthenticationManager的实现是ProviderManager.

在这里插入图片描述

ProviderManager

  ProviderManagerAuthenticationManager的默认实现,但是ProviderManager并没有提供具体的认证逻辑,而是具有多个AuthenticationProvider.
也就是在ProviderManager中支持多种认证方式,而AuthenticationProvider就是一种具体的认证。

  1. public class ProviderManager implements AuthenticationManager, MessageSourceAware,
  2. InitializingBean {
  3. // 多种认证方式
  4. private List<AuthenticationProvider> providers = Collections.emptyList();
  5. private AuthenticationManager parent;
  6. private boolean eraseCredentialsAfterAuthentication = true;
  7. /** * 遍历每一种认证方式,进行认证 **/
  8. public Authentication authenticate(Authentication authentication)
  9. throws AuthenticationException {
  10. Class<? extends Authentication> toTest = authentication.getClass();
  11. AuthenticationException lastException = null;
  12. AuthenticationException parentException = null;
  13. Authentication result = null;
  14. Authentication parentResult = null;
  15. boolean debug = logger.isDebugEnabled();
  16. // 遍历每一种认证方式
  17. for (AuthenticationProvider provider : getProviders()) {
  18. if (!provider.supports(toTest)) {
  19. continue;
  20. }
  21. // 忽略...
  22. try {
  23. // 调用具体的认证方法
  24. result = provider.authenticate(authentication);
  25. if (result != null) {
  26. copyDetails(authentication, result);
  27. break;
  28. }
  29. }
  30. catch (AccountStatusException | InternalAuthenticationServiceException e) {
  31. prepareException(e, authentication);
  32. throw e;
  33. } catch (AuthenticationException e) {
  34. lastException = e;
  35. }
  36. }
  37. // 忽略...
  38. }
  39. }

在这里插入图片描述

  通过上图可以简单的体现这三者之间的关系

AuthenticationProvider

  现在我们来看下AuthenticationProvider的具体认证流程的实现

  1. public interface AuthenticationProvider {
  2. /** * 认证逻辑实现的方法 */
  3. Authentication authenticate(Authentication authentication)
  4. throws AuthenticationException;
  5. /** * 判断当前provider是否支持该Authentication * / boolean supports(Class<?> authentication); }

  源码中的方法相比AuthenticationManager中多了一个supports方法,主要是用来做支持判断的。具体是认证实现还要看其具体的实现,默认的实现是DaoAuthenticationProvider,还有个中间抽象类AbstractUserDetailsAuthenticationProvider

在这里插入图片描述

AbstractUserDetailsAuthenticationProvider

  在其中定义了认证的主要流程

  1. public abstract class AbstractUserDetailsAuthenticationProvider implements
  2. AuthenticationProvider, InitializingBean, MessageSourceAware {
  3. // 抽象定义
  4. protected abstract void additionalAuthenticationChecks(UserDetails userDetails,
  5. UsernamePasswordAuthenticationToken authentication)
  6. throws AuthenticationException;
  7. // 定义主要流程,关键账号验证和密码验证在具体实现类中实现
  8. public Authentication authenticate(Authentication authentication)
  9. throws AuthenticationException {
  10. Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
  11. () -> messages.getMessage(
  12. "AbstractUserDetailsAuthenticationProvider.onlySupports",
  13. "Only UsernamePasswordAuthenticationToken is supported"));
  14. // Determine username
  15. String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
  16. : authentication.getName();
  17. boolean cacheWasUsed = true;
  18. UserDetails user = this.userCache.getUserFromCache(username);
  19. if (user == null) {
  20. cacheWasUsed = false;
  21. try {
  22. user = retrieveUser(username,
  23. (UsernamePasswordAuthenticationToken) authentication);
  24. }
  25. catch (UsernameNotFoundException notFound) {
  26. logger.debug("User '" + username + "' not found");
  27. if (hideUserNotFoundExceptions) {
  28. throw new BadCredentialsException(messages.getMessage(
  29. "AbstractUserDetailsAuthenticationProvider.badCredentials",
  30. "Bad credentials"));
  31. }
  32. else {
  33. throw notFound;
  34. }
  35. }
  36. Assert.notNull(user,
  37. "retrieveUser returned null - a violation of the interface contract");
  38. }
  39. try {
  40. preAuthenticationChecks.check(user);
  41. additionalAuthenticationChecks(user,
  42. (UsernamePasswordAuthenticationToken) authentication);
  43. }
  44. catch (AuthenticationException exception) {
  45. if (cacheWasUsed) {
  46. // There was a problem, so try again after checking
  47. // we're using latest data (i.e. not from the cache)
  48. cacheWasUsed = false;
  49. user = retrieveUser(username,
  50. (UsernamePasswordAuthenticationToken) authentication);
  51. preAuthenticationChecks.check(user);
  52. additionalAuthenticationChecks(user,
  53. (UsernamePasswordAuthenticationToken) authentication);
  54. }
  55. else {
  56. throw exception;
  57. }
  58. }
  59. postAuthenticationChecks.check(user);
  60. if (!cacheWasUsed) {
  61. this.userCache.putUserInCache(user);
  62. }
  63. Object principalToReturn = user;
  64. if (forcePrincipalAsString) {
  65. principalToReturn = user.getUsername();
  66. }
  67. return createSuccessAuthentication(principalToReturn, authentication, user);
  68. }
  69. UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
  70. principal, authentication.getCredentials(),
  71. authoritiesMapper.mapAuthorities(user.getAuthorities()));
  72. result.setDetails(authentication.getDetails());
  73. return result;
  74. }
  75. // 抽象的 账号验证
  76. protected abstract UserDetails retrieveUser(String username,
  77. UsernamePasswordAuthenticationToken authentication)
  78. throws AuthenticationException;
  79. public boolean supports(Class<?> authentication) {
  80. return (UsernamePasswordAuthenticationToken.class
  81. .isAssignableFrom(authentication));
  82. }
  83. }

DaoAuthenticationProvider

  默认的具体认证实现

  1. protected final UserDetails retrieveUser(String username,
  2. UsernamePasswordAuthenticationToken authentication)
  3. throws AuthenticationException {
  4. prepareTimingAttackProtection();
  5. try {
  6. UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
  7. if (loadedUser == null) {
  8. throw new InternalAuthenticationServiceException(
  9. "UserDetailsService returned null, which is an interface contract violation");
  10. }
  11. return loadedUser;
  12. }
  13. catch (UsernameNotFoundException ex) {
  14. mitigateAgainstTimingAttack(authentication);
  15. throw ex;
  16. }
  17. catch (InternalAuthenticationServiceException ex) {
  18. throw ex;
  19. }
  20. catch (Exception ex) {
  21. throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
  22. }
  23. }
  24. @SuppressWarnings("deprecation")
  25. protected void additionalAuthenticationChecks(UserDetails userDetails,
  26. UsernamePasswordAuthenticationToken authentication)
  27. throws AuthenticationException {
  28. if (authentication.getCredentials() == null) {
  29. logger.debug("Authentication failed: no credentials provided");
  30. throw new BadCredentialsException(messages.getMessage(
  31. "AbstractUserDetailsAuthenticationProvider.badCredentials",
  32. "Bad credentials"));
  33. }
  34. String presentedPassword = authentication.getCredentials().toString();
  35. if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
  36. logger.debug("Authentication failed: password does not match stored value");
  37. throw new BadCredentialsException(messages.getMessage(
  38. "AbstractUserDetailsAuthenticationProvider.badCredentials",
  39. "Bad credentials"));
  40. }
  41. }

在这里插入图片描述
  上图是整理的相关的整体的结构,通过上图应该能够对于相关的结构关系会有一个整体的认证,下篇文章我们会给大家梳理下初始化的过程

发表评论

表情:
评论列表 (有 0 条评论,417人围观)

还没有评论,来说两句吧...

相关阅读