How does shiro verify the encryption process?

In the single-step debugging of the shiro login source code combined with Zhang Kaitao's demo, I learned how the shiro verification encryption process is.
Complete the password verification of the login form submission in the getAuthenticationInfo(AuthenticationToken token) method of the core class AuthenticatingRealm.
Schematic diagram of shiro class method call


1. getAuthenticationInfo core method
    public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//The incoming AuthenticationToken token is a class encapsulated by the form submission parameters, which stores the username, plaintext password, remember me, and host IP.
        AuthenticationInfo info = getCachedAuthenticationInfo(token);
        if (info == null) {
//Get AuthenticationInfo (usually a database) from a custom UserRealm, and use it as a comparison sample. The purpose is to compare the ciphertext generated by the token with the password and ciphertext of this sample. If they are consistent, the login is successful.
            //otherwise not cached, perform the lookup:
            info = doGetAuthenticationInfo(token);
            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
            if (token != null && info != null) {
                cacheAuthenticationInfoIfPossible(token, info);
            }
        } else {
            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
        }

        if (info != null) {
//The comparison is done in the body of this method
            assertCredentialsMatch(token, info);
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;
    }

2.doGetAuthenticationInfo, custom realm, according to the user name in the token, get the ciphertext, salt, realm name from the database and encapsulate it into info
@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        String username = (String)token.getPrincipal();

        User user = userService.findByUsername(username);

        if(user == null) {
            throw new UnknownAccountException();//No account found
        }

        if(Boolean.TRUE.equals(user.getLocked())) {
            throw new LockedAccountException(); //Account locked
        }

        //Give it to AuthenticatingRealm to use CredentialsMatcher for password matching. If you feel that others are not good, you can customize the implementation
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUsername(), //username
                user.getPassword(), //password
                ByteSource.Util.bytes (user.getCredentialsSalt ()), // salt = username + salt
                getName()  //realm name
        );
        return authenticationInfo;
    }

3.assertCredentialsMatch, credential matcher, the submitted credentials and stored credentials are matched and compared
//Asserts that the submitted AuthenticationToken's credentials match the stored account AuthenticationInfo's credentials
//My understanding of the translation is that the submitted credentials and the stored credentials are matched and compared
    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
//Return the set credential matcher (matching rule), including hashAlgorithmName (encryption method name such as md5), hashIterations (encryption times), storedCredentialsHexEncoded (ciphertext is stored in hexadecimal), hashSalted (default value false), passwordRetryCache (Password Retry Cache) 5 properties.
        CredentialsMatcher cm = getCredentialsMatcher()
        if (cm != null) {
//Perform ciphertext matching. At this point, the password in the token is still in plaintext, not encrypted
            if (!cm.doCredentialsMatch(token, info)) {
                //not successful - throw an exception to indicate this:
                String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
                throw new IncorrectCredentialsException(msg);
            }
        } else {
            throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
                    "credentials during authentication.  If you do not wish for credentials to be examined, you " +
                    "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
        }
    }

4. doCredentialsMatch, the doCredentialsMatch of the parent class HashedCredentialsMatcher after the number of password errors
//Add the number of password errors to the doCredentialsMatch method of the custom RetryLimitHashedCredentialsMatcher and enter the doCredentialsMatch of the parent class HashedCredentialsMatcher.
// parent class doCredentialsMatch
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        Object tokenHashedCredentials = hashProvidedCredentials(token, info);
        Object accountCredentials = getCredentials(info);
        return equals(tokenHashedCredentials, accountCredentials);
    }

5.hashProvidedCredentials, take out the salt from the info, and a hash that needs to be authenticated with the password submitted by the form
// remove salt
protected Object hashProvidedCredentials(AuthenticationToken token, AuthenticationInfo info) {
        Object salt = null;
        if (info instanceof SaltedAuthenticationInfo) {
            salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
        } else {
            //retain 1.0 backwards compatibility:
            if (isHashSalted()) {
                salt = getSalt(token);
            }
        }
        return hashProvidedCredentials(token.getCredentials(), salt, getHashIterations());
    }
//generate hash
//Object credentials username
//Object salt salt
// int hashIterations hash degree
    protected Hash hashProvidedCredentials(Object credentials, Object salt, int hashIterations) {
//Algorithm name, such as md5
        String hashAlgorithmName = assertHashAlgorithmName();
//The ciphertext generated when creating a user account also generates a SimpleHash, with one more hashIterations for the construction parameter
        return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
    }

6.getCredentials, extract the ciphertext from the stored info and convert it into a hash object
//    protected Object getCredentials(AuthenticationInfo info) {
        Object credentials = info.getCredentials();

        byte[] storedBytes = toBytes(credentials);

        if (credentials instanceof String || credentials instanceof char[]) {
            //account.credentials were a char[] or String, so
            //we need to do text decoding first:
            if (isStoredCredentialsHexEncoded()) {
                storedBytes = Hex.decode(storedBytes);
            } else {
                storedBytes = Base64.decode(storedBytes);
            }
        }
        AbstractHash hash = newHashInstance();
        hash.setBytes(storedBytes);
        return hash;
    }
//Compare two hash objects

7.equals, compare two hash objects
    protected boolean equals(Object tokenCredentials, Object accountCredentials) {
        if (log.isDebugEnabled()) {
            log.debug("Performing credentials equality check for tokenCredentials of type [" +
                    tokenCredentials.getClass().getName() + " and accountCredentials of type [" +
                    accountCredentials.getClass().getName() + "]");
        }
        if (isByteSource(tokenCredentials) && isByteSource(accountCredentials)) {
            if (log.isDebugEnabled()) {
                log.debug("Both credentials arguments can be easily converted to byte arrays.  Performing " +
                        "array equals comparison");
            }
//Finally converted to byte array for comparison
            byte[] tokenBytes = toBytes(tokenCredentials);
            byte[] accountBytes = toBytes(accountCredentials);
            return Arrays.equals(tokenBytes, accountBytes);
        } else {
            return accountCredentials.equals(tokenCredentials);
        }
    }

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326416913&siteId=291194637