学习 准备 尝试 谨慎小心

0%

Shiro源码解析之Realm

了解 Realm

继承图

UserRealm

Realm

1571724441972

CachingRealm

1571724474817

AuthenticatingRealm

AuthenticatingRealm

AuthorizingRealm

AuthorizingRealm

UserRealm

通常我们自定义的 realm——UserRealm

1571734551679

理解Realm

getAuthenticationInfo(AuthenticationToken): AuthenticationInfo

1. AuthenticationRealm | getAuthenticationInfo

1
// line 541
2
/**
3
 * This implementation functions as follows:
4
 * <ol>
5
 * <li>It attempts to acquire any cached {@link AuthenticationInfo} corresponding to the specified
6
 * {@link AuthenticationToken} argument.  If a cached value is found, it will be used for credentials matching,
7
 * alleviating the need to perform any lookups with a data source.</li>
8
 * <li>If there is no cached {@link AuthenticationInfo} found, delegate to the
9
 * {@link #doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)} method to perform the actual
10
 * lookup.  If authentication caching is enabled and possible, any returned info object will be
11
 * {@link #cacheAuthenticationInfoIfPossible(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.authc.AuthenticationInfo) cached}
12
 * to be used in future authentication attempts.</li>
13
 * <li>If an AuthenticationInfo instance is not found in the cache or by lookup, {@code null} is returned to
14
 * indicate an account cannot be found.</li>
15
 * <li>If an AuthenticationInfo instance is found (either cached or via lookup), ensure the submitted
16
 * AuthenticationToken's credentials match the expected {@code AuthenticationInfo}'s credentials using the
17
 * {@link #getCredentialsMatcher() credentialsMatcher}.  This means that credentials are always verified
18
 * for an authentication attempt.</li>
19
 * </ol>
20
 *
21
 * @param token the submitted account principal and credentials.
22
 * @return the AuthenticationInfo corresponding to the given {@code token}, or {@code null} if no
23
 *         AuthenticationInfo could be found.
24
 * @throws AuthenticationException if authentication failed.
25
 */
26
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
27
28
    AuthenticationInfo info = getCachedAuthenticationInfo(token);
29
    if (info == null) {
30
        //otherwise not cached, perform the lookup:
31
        info = doGetAuthenticationInfo(token);
32
        log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
33
        if (token != null && info != null) {
34
            cacheAuthenticationInfoIfPossible(token, info);
35
        }
36
    } else {
37
        log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
38
    }
39
40
    if (info != null) {
41
        assertCredentialsMatch(token, info);
42
    } else {
43
        log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
44
    }
45
46
    return info;
47
}
48
......
49
// line 696
50
/**
51
 * Retrieves authentication data from an implementation-specific datasource (RDBMS, LDAP, etc) for the given
52
 * authentication token.
53
 * <p/>
54
 * For most datasources, this means just 'pulling' authentication data for an associated subject/user and nothing
55
 * more and letting Shiro do the rest.  But in some systems, this method could actually perform EIS specific
56
 * log-in logic in addition to just retrieving data - it is up to the Realm implementation.
57
 * <p/>
58
 * A {@code null} return value means that no account could be associated with the specified token.
59
 *
60
 * @param token the authentication token containing the user's principal and credentials.
61
 * @return an {@link AuthenticationInfo} object containing account data resulting from the
62
 *         authentication ONLY if the lookup is successful (i.e. account exists and is valid, etc.)
63
 * @throws AuthenticationException if there is an error acquiring data or performing
64
 *                                 realm-specific authentication logic for the specified <tt>token</tt>
65
 */
66
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

2. AuthenticationRealm | getCachedAuthenticationInfo(token)

1
// line 471
2
3
/**
4
 * Returns any cached AuthenticationInfo corresponding to the specified token or {@code null} if there currently
5
 * isn't any cached data.
6
 *
7
 * @param token the token submitted during the authentication attempt.
8
 * @return any cached AuthenticationInfo corresponding to the specified token or {@code null} if there currently
9
 *         isn't any cached data.
10
 * @since 1.2
11
 */
12
private AuthenticationInfo getCachedAuthenticationInfo(AuthenticationToken token) {
13
    AuthenticationInfo info = null;
14
15
    Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
16
    if (cache != null && token != null) {
17
        log.trace("Attempting to retrieve the AuthenticationInfo from cache.");
18
        Object key = getAuthenticationCacheKey(token);
19
        info = cache.get(key);
20
        if (info == null) {
21
            log.trace("No AuthorizationInfo found in cache for key [{}]", key);
22
        } else {
23
            log.trace("Found cached AuthorizationInfo for key [{}]", key);
24
        }
25
    }
26
27
    return info;
28
}
29
...... 
30
// line 419
31
/**
32
 * Returns any available {@link Cache} instance to use for authentication caching.  This functions as follows:
33
 * <ol>
34
 * <li>If an {@link #setAuthenticationCache(org.apache.shiro.cache.Cache) authenticationCache} has been explicitly
35
 * configured (it is not null), it is returned.</li>
36
 * <li>If there is no {@link #getAuthenticationCache() authenticationCache} configured:
37
 * <ol>
38
 * <li>If authentication caching is {@link #isAuthenticationCachingEnabled() enabled}, any available
39
 * {@link #getCacheManager() cacheManager} will be consulted to obtain an available authentication cache.
40
 * </li>
41
 * <li>If authentication caching is disabled, this implementation does nothing.</li>
42
 * </ol>
43
 * </li>
44
 * </ol>
45
 *
46
 * @return any available {@link Cache} instance to use for authentication caching.
47
 */
48
private Cache<Object, AuthenticationInfo> getAvailableAuthenticationCache() {
49
    Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
50
    boolean authcCachingEnabled = isAuthenticationCachingEnabled();
51
    if (cache == null && authcCachingEnabled) {
52
        cache = getAuthenticationCacheLazy();
53
    }
54
    return cache;
55
}
56
57
/**
58
 * Checks to see if the authenticationCache class attribute is null, and if so, attempts to acquire one from
59
 * any configured {@link #getCacheManager() cacheManager}.  If one is acquired, it is set as the class attribute.
60
 * The class attribute is then returned.
61
 *
62
 * @return an available cache instance to be used for authentication caching or {@code null} if one is not available.
63
 * @since 1.2
64
 */
65
private Cache<Object, AuthenticationInfo> getAuthenticationCacheLazy() {
66
67
    if (this.authenticationCache == null) {
68
69
        log.trace("No authenticationCache instance set.  Checking for a cacheManager...");
70
71
        CacheManager cacheManager = getCacheManager();
72
73
        if (cacheManager != null) {
74
            String cacheName = getAuthenticationCacheName();
75
            log.debug("CacheManager [{}] configured.  Building authentication cache '{}'", cacheManager, cacheName);
76
            this.authenticationCache = cacheManager.getCache(cacheName);
77
        }
78
    }
79
80
    return this.authenticationCache;
81
}
82
......
83
// line 611
84
/**
85
 * Returns the key under which {@link AuthenticationInfo} instances are cached if authentication caching is enabled.
86
 * This implementation defaults to returning the token's
87
 * {@link org.apache.shiro.authc.AuthenticationToken#getPrincipal() principal}, which is usually a username in
88
 * most applications.
89
 * <h3>Cache Invalidation on Logout</h3>
90
 * <b>NOTE:</b> If you want to be able to invalidate an account's cached {@code AuthenticationInfo} on logout, you
91
 * must ensure the {@link #getAuthenticationCacheKey(org.apache.shiro.subject.PrincipalCollection)} method returns
92
 * the same value as this method.
93
 *
94
 * @param token the authentication token for which any successful authentication will be cached.
95
 * @return the cache key to use to cache the associated {@link AuthenticationInfo} after a successful authentication.
96
 * @since 1.2
97
 */
98
protected Object getAuthenticationCacheKey(AuthenticationToken token) {
99
    return token != null ? token.getPrincipal() : null;
100
}

从缓存 authenticationCache 中获取 authenticationInfo,从 cacheManager 中获取缓存。

问题:

  1. 是什么时候将 authenticationInfo 存入缓存的?

3. UserRealm | doGetAuthenticationInfo(token)

自定义Realm实现 doGetAuthenticationInfo(token) 方法

1
/**
2
 * 登录认证
3
 */
4
@Override
5
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
6
{
7
    UsernamePasswordToken upToken = (UsernamePasswordToken) token;
8
    String username = upToken.getUsername();
9
    String password = "";
10
    if (upToken.getPassword() != null)
11
    {
12
        password = new String(upToken.getPassword());
13
    }
14
15
    SysUser user = null;
16
    try
17
    {
18
        user = loginService.login(username, password);
19
    }
20
    catch (CaptchaException e)
21
    {
22
        throw new AuthenticationException(e.getMessage(), e);
23
    }
24
    catch (UserNotExistsException e)
25
    {
26
        throw new UnknownAccountException(e.getMessage(), e);
27
    }
28
    catch (UserPasswordNotMatchException e)
29
    {
30
        throw new IncorrectCredentialsException(e.getMessage(), e);
31
    }
32
    catch (UserPasswordRetryLimitExceedException e)
33
    {
34
        throw new ExcessiveAttemptsException(e.getMessage(), e);
35
    }
36
    catch (UserBlockedException e)
37
    {
38
        throw new LockedAccountException(e.getMessage(), e);
39
    }
40
    catch (RoleBlockedException e)
41
    {
42
        throw new LockedAccountException(e.getMessage(), e);
43
    }
44
    catch (Exception e)
45
    {
46
        log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
47
        throw new AuthenticationException(e.getMessage(), e);
48
    }
49
    SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
50
    return info;
51
}

SimpleAuthenticationInfo 是一个比较有意思的类,在下面会有具体的分析

4. AuthenticationRealm | cacheAuthenticationInfoIfPossible(token, info)

1
// line 498
2
/**
3
 * Caches the specified info if authentication caching
4
 * {@link #isAuthenticationCachingEnabled(org.apache.shiro.authc.AuthenticationToken, org.apache.shiro.authc.AuthenticationInfo) isEnabled}
5
 * for the specific token/info pair and a cache instance is available to be used.
6
 *
7
 * @param token the authentication token submitted which resulted in a successful authentication attempt.
8
 * @param info  the AuthenticationInfo to cache as a result of the successful authentication attempt.
9
 * @since 1.2
10
 */
11
private void cacheAuthenticationInfoIfPossible(AuthenticationToken token, AuthenticationInfo info) {
12
    if (!isAuthenticationCachingEnabled(token, info)) {
13
        log.debug("AuthenticationInfo caching is disabled for info [{}].  Submitted token: [{}].", info, token);
14
        //return quietly, caching is disabled for this token/info pair:
15
        return;
16
    }
17
18
    Cache<Object, AuthenticationInfo> cache = getAvailableAuthenticationCache();
19
    if (cache != null) {
20
        Object key = getAuthenticationCacheKey(token);
21
        cache.put(key, info);
22
        log.trace("Cached AuthenticationInfo for continued authentication.  key=[{}], value=[{}].", key, info);
23
    }
24
}

将 authenticationInfo 存入缓存中,不过 isAuthenticationCachingEnabled(token, info) 默认是 false。

5. AuthenticationRealm | assertCredentialsMatch

1
/**
2
 * Asserts that the submitted {@code AuthenticationToken}'s credentials match the stored account
3
 * {@code AuthenticationInfo}'s credentials, and if not, throws an {@link AuthenticationException}.
4
 *
5
 * @param token the submitted authentication token
6
 * @param info  the AuthenticationInfo corresponding to the given {@code token}
7
 * @throws AuthenticationException if the token's credentials do not match the stored account credentials.
8
 */
9
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
10
    CredentialsMatcher cm = getCredentialsMatcher();
11
    if (cm != null) {
12
        if (!cm.doCredentialsMatch(token, info)) {
13
            //not successful - throw an exception to indicate this:
14
            String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials.";
15
            throw new IncorrectCredentialsException(msg);
16
        }
17
    } else {
18
        throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " +
19
                "credentials during authentication.  If you do not wish for credentials to be examined, you " +
20
                "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
21
    }
22
}

小结

  1. 从缓存中获取 AuthenticationInfo,如果开启了 AuthenticationInfo 缓存的话(AuthenticationRealm)
  2. 调用 doGetAuthenticationInfo(token) 获取 AuthentionInfo (AuthenticationRealm)
  3. 如果开启了 AuthenticationInfo 缓存的话,那么缓存它
  4. 调用 assertCredentialsMatch 判断 token 和 info 是否匹配
  5. 返回 AuthenticationInfo