安全/权限框架Shiro(7)之密码比对原理

安全/权限框架Shiro(7)之密码比对原理

微信搜索 zze_coding 或扫描 👉 二维码关注我的微信公众号获取更多资源推送:

Shiro 是如何完成密码的比对的呢?

我们已经知道,从页面传入的 usernamepassword 已经被封装在 UsernamePasswordToken 实例中通过 Subject.login(AuthenticationToken) 方法交给了 Shiro 传递到了 LoginRealm,而我们在 LoginRealm 中返回的校验信息是正确的用户名及密码信息。可以推断,Shiro 所做的比对其实就是将页面传入的 UsernamePasswordToken 实例和我们返回的正确的校验信息 AuthenticationInfo 实例中的数据进行比对,所以我们可以在返回 SimpleAuthenticationInfo 实例时打个断点,跟着进入源码,肯定能找到 Shiro 是如何完成比对。

image.png

下一步,进到调用 doGetAuthenticationInfo 的方法中:

// org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

    AuthenticationInfo info = getCachedAuthenticationInfo(token);
    if (info == null) {
        //otherwise not cached, perform the lookup:
        // <1>
        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) {
        // <2>
        assertCredentialsMatch(token, info);
    } else {
        log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
    }

    return info;
}

可以看到,在 <1> 处拿到我们返回的 SimpleAuthenticationInfo 实例,接着将 UsernamePasswordToken 实例和 SimpleAuthenticationInfo 实例一起传入 <2> 处的 assertCredentialsMatch(token, info) 方法:

// org.apache.shiro.realm.AuthenticatingRealm#assertCredentialsMatch
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
    CredentialsMatcher cm = getCredentialsMatcher();
    if (cm != null) {
        // <3>
        if (!cm.doCredentialsMatch(token, info)) {
            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.");
    }
}

再看到 <3> 处的 cm.doCredentialsMatch(token, info) 方法,通过方法名就可以看出这是个凭证匹配的方法,依旧是将 UsernamePasswordToken 实例和 SimpleAuthenticationInfo 实例一起传入了:

public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    Object tokenCredentials = getCredentials(token);
    Object accountCredentials = getCredentials(info);
    return equals(tokenCredentials, accountCredentials);
}

一目了然, 拿到页面传入的密码和正确的密码进行比对,返回布尔值。接着回到 org.apache.shiro.realm.AuthenticatingRealm#assertCredentialsMatch 方法中,如果比对失败,则抛出 org.apache.shiro.authc.AuthenticationException 异常。

总结:Shiro 是通过 org.apache.shiro.authc.credential.CredentialsMatcher 类实例的 doCredentialsMatch 方法来完成页面传入的密码和实际的密码的比对。

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://www.zze.xyz/archives/shiro7.html

Buy me a cup of coffee ☕.