天天看點

SpringSecurity 密碼驗證流程一、通過debug源碼,解析,更加清楚的了解核心的原理,出現問題可以更好的解決二、密碼驗證流程三、其他有關SpringSecurity的筆記,包括核心認識,demo位址

一、通過debug源碼,解析,更加清楚的了解核心的原理,出現問題可以更好的解決

二、密碼驗證流程

1 預設使用 UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter

  • 執行方法:
    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); (這裡最終是AbstractAuthenticationToken)
        return this.getAuthenticationManager().authenticate(authRequest);
    }            

2 return this.getAuthenticationManager().authenticate(authRequest); 執行這個方法後,跳入到 AuthenticationManager 這個接口,以其一個實作類來實作這個方法。

Authentication authenticate(Authentication authentication) throws AuthenticationException;           

3 ProviderManager implements AuthenticationManager, MessageSourceAware,InitializingBean

public Authentication authenticate(Authentication authentication)
               throws AuthenticationException {
           Class<? extends Authentication> toTest = authentication.getClass();
           AuthenticationException lastException = null;
           Authentication result = null;
           boolean debug = logger.isDebugEnabled(); 
           --AuthenticationProvider 校驗的是這個對象
           for (AuthenticationProvider provider : getProviders()) {
               if (!provider.supports(toTest)) {
                   continue;
               }
               if (debug) {
                   logger.debug("Authentication attempt using "
                           + provider.getClass().getName());
               }
               try {
                    -- 重點,這裡調用了驗證方法 ----  
                   result = provider.authenticate(authentication);         
                   if (result != null) {
                       copyDetails(authentication, result);
                       break;
                   }
               }
               catch (AccountStatusException e) {
                   prepareException(e, authentication);
                   // SEC-546: Avoid polling additional providers if auth failure is due to
                   // invalid account status
                   throw e;
               }
               catch (InternalAuthenticationServiceException e) {
                   prepareException(e, authentication);
                   throw e;
               }
               catch (AuthenticationException e) {
                   lastException = e;
               }
           }
           if (result == null && parent != null) {
               // Allow the parent to try.
               try {
                   result = parent.authenticate(authentication);
               }
               catch (ProviderNotFoundException e) {
                   // ignore as we will throw below if no other exception occurred prior to
                   // calling parent and the parent
                   // may throw ProviderNotFound even though a provider in the child already
                   // handled the request
               }
               catch (AuthenticationException e) {
                   lastException = e;
               }
           }
           if (result != null) {
               if (eraseCredentialsAfterAuthentication
                       && (result instanceof CredentialsContainer)) {
                   // Authentication is complete. Remove credentials and other secret data
                   // from authentication
                   ((CredentialsContainer) result).eraseCredentials();
               }
               eventPublisher.publishAuthenticationSuccess(result);
               return result;
           }
           // Parent was null, or didn't authenticate (or throw an exception)
           if (lastException == null) {
               lastException = new ProviderNotFoundException(messages.getMessage(
                       "ProviderManager.providerNotFound",
                       new Object[] { toTest.getName() },
                       "No AuthenticationProvider found for {0}"));
           }
           prepareException(lastException, authentication);
           throw lastException;
    }           

4 進入 public interface AuthenticationProvider

Authentication authenticate(Authentication authentication) throws AuthenticationException;           

5 進入 abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware,實作了AuthenticationProvider 接口的authenticate(authentication)方法

public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
                messages.getMessage(
                        "AbstractUserDetailsAuthenticationProvider.onlySupports",
                        "Only UsernamePasswordAuthenticationToken is supported"));
        // Determine username
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED": authentication.getName();
        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);
        if (user == null) {
            cacheWasUsed = false;
            try {
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            catch (UsernameNotFoundException notFound) {
                logger.debug("User '" + username + "' not found");
                if (hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(messages.getMessage(
                            "AbstractUserDetailsAuthenticationProvider.badCredentials",
                            "Bad credentials"));
                }
                else {
                    throw notFound;
                }
            }
            Assert.notNull(user,
                    "retrieveUser returned null - a violation of the interface contract");
        }
        try {
            preAuthenticationChecks.check(user);
            additionalAuthenticationChecks(user,
                    (UsernamePasswordAuthenticationToken) authentication);
        }
        catch (AuthenticationException exception) {
            if (cacheWasUsed) {
                // There was a problem, so try again after checking
                // we're using latest data (i.e. not from the cache)
                cacheWasUsed = false;
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
                preAuthenticationChecks.check(user);
                additionalAuthenticationChecks(user,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            else {
                throw exception;
            }
        }
        postAuthenticationChecks.check(user);
        if (!cacheWasUsed) {
            this.userCache.putUserInCache(user);
        }
        Object principalToReturn = user;
        if (forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }
        return createSuccessAuthentication(principalToReturn, authentication, user);
    }           
  • 在方法中調用自身的一個抽象方法;
    protected abstract void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication)
         throws AuthenticationException;           

6 通過實作該抽象類的子類 class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider,重寫 additionalAuthenticationChecks方法

protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        Object salt = null;
        if (this.saltSource != null) {
            salt = this.saltSource.getSalt(userDetails);
        }
        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
        String presentedPassword = authentication.getCredentials().toString();
        //驗證密碼, passwordEncoder 是用戶端穿回來的密碼,userDetails.getPassword()是資料庫中的密碼
        if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),presentedPassword, salt)) {
            logger.debug("Authentication failed: password does not match stored value");
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
    }           

三、其他有關SpringSecurity的筆記,包括核心認識,demo位址

SpringSecurity初步學習