了解 Realm
继承图

Realm

CachingRealm

AuthenticatingRealm

AuthorizingRealm

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

理解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 中获取缓存。
问题:
- 是什么时候将 authenticationInfo 存入缓存的?
3. UserRealm | doGetAuthenticationInfo(token)
自定义Realm实现 doGetAuthenticationInfo(token) 方法
1 | /** |
2 | * 登录认证 |
3 | */ |
4 | |
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 | } |
小结
- 从缓存中获取 AuthenticationInfo,如果开启了 AuthenticationInfo 缓存的话(AuthenticationRealm)
- 调用 doGetAuthenticationInfo(token) 获取 AuthentionInfo (AuthenticationRealm)
- 如果开启了 AuthenticationInfo 缓存的话,那么缓存它
- 调用 assertCredentialsMatch 判断 token 和 info 是否匹配
- 返回 AuthenticationInfo