Spring security password verification process

Keywords: Java Spring Database

I. through debug ging source code, analysis, and a clearer understanding of the core principles, problems can be better solved.

II. Password verification process

1. Usernamepasswordauthenticationfilter extensions abstractauthenticationprocessingfilter is used by default

  • Execution method:

    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); (And here it turns out AbstractAuthenticationToken)
        return this.getAuthenticationManager().authenticate(authRequest);
    } 

2 return this.getAuthenticationManager().authenticate(authRequest); after executing this method, jump into the AuthenticationManager interface to implement this method with one of its implementation classes.

   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 This object is verified
           for (AuthenticationProvider provider : getProviders()) {
               if (!provider.supports(toTest)) {
                   continue;
               }
               if (debug) {
                   logger.debug("Authentication attempt using "
                           + provider.getClass().getName());
               }
               try {
                    -- A key,The validation method is called here ----  
                   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 enter public interface AuthenticationProvider

    Authentication authenticate(Authentication authentication) throws AuthenticationException;

5. Enter abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware, and implement the authentication (authentication) method of the AuthenticationProvider interface.

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);
    }
  • Invoking an abstract method in the method;

    protected abstract void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication)
         throws AuthenticationException;

6. Override the additionalAuthenticationChecks method by implementing the subclass class daoauthenticationprovider extensions abstractuserdetailsauthenticationprovider of the abstract class

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();
        //Verify the password. passwordEncoder is the password put back by the client. userDetails.getPassword() is the password in the database.
        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"));
        }
    }

III. other notes about spring security, including core knowledge and demo address

Spring security preliminary learning

Posted by kishanprasad on Tue, 22 Oct 2019 09:04:07 -0700