了解 Authenticator
Authenticator 用来进行身份认证,它的继承图以及继承者的方法如下:

理解 Authenticator
1. AbstractAuthenticator | authencate(token)
1 | // line 167 |
2 | /** |
3 | * Implementation of the {@link Authenticator} interface that functions in the following manner: |
4 | * <ol> |
5 | * <li>Calls template {@link #doAuthenticate doAuthenticate} method for subclass execution of the actual |
6 | * authentication behavior.</li> |
7 | * <li>If an {@code AuthenticationException} is thrown during {@code doAuthenticate}, |
8 | * {@link #notifyFailure(AuthenticationToken, AuthenticationException) notify} any registered |
9 | * {@link AuthenticationListener AuthenticationListener}s of the exception and then propagate the exception |
10 | * for the caller to handle.</li> |
11 | * <li>If no exception is thrown (indicating a successful login), |
12 | * {@link #notifySuccess(AuthenticationToken, AuthenticationInfo) notify} any registered |
13 | * {@link AuthenticationListener AuthenticationListener}s of the successful attempt.</li> |
14 | * <li>Return the {@code AuthenticationInfo}</li> |
15 | * </ol> |
16 | * |
17 | * @param token the submitted token representing the subject's (user's) login principals and credentials. |
18 | * @return the AuthenticationInfo referencing the authenticated user's account data. |
19 | * @throws AuthenticationException if there is any problem during the authentication process - see the |
20 | * interface's JavaDoc for a more detailed explanation. |
21 | */ |
22 | public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException { |
23 | |
24 | if (token == null) { |
25 | throw new IllegalArgumentException("Method argument (authentication token) cannot be null."); |
26 | } |
27 | |
28 | log.trace("Authentication attempt received for token [{}]", token); |
29 | |
30 | AuthenticationInfo info; |
31 | try { |
32 | info = doAuthenticate(token); |
33 | if (info == null) { |
34 | String msg = "No account information found for authentication token [" + token + "] by this " + |
35 | "Authenticator instance. Please check that it is configured correctly."; |
36 | throw new AuthenticationException(msg); |
37 | } |
38 | } catch (Throwable t) { |
39 | AuthenticationException ae = null; |
40 | if (t instanceof AuthenticationException) { |
41 | ae = (AuthenticationException) t; |
42 | } |
43 | if (ae == null) { |
44 | //Exception thrown was not an expected AuthenticationException. Therefore it is probably a little more |
45 | //severe or unexpected. So, wrap in an AuthenticationException, log to warn, and propagate: |
46 | String msg = "Authentication failed for token submission [" + token + "]. Possible unexpected " + |
47 | "error? (Typical or expected login exceptions should extend from AuthenticationException)."; |
48 | ae = new AuthenticationException(msg, t); |
49 | if (log.isWarnEnabled()) |
50 | log.warn(msg, t); |
51 | } |
52 | try { |
53 | notifyFailure(token, ae); |
54 | } catch (Throwable t2) { |
55 | if (log.isWarnEnabled()) { |
56 | String msg = "Unable to send notification for failed authentication attempt - listener error?. " + |
57 | "Please check your AuthenticationListener implementation(s). Logging sending exception " + |
58 | "and propagating original AuthenticationException instead..."; |
59 | log.warn(msg, t2); |
60 | } |
61 | } |
62 | |
63 | |
64 | throw ae; |
65 | } |
66 | |
67 | log.debug("Authentication successful for token [{}]. Returned account [{}]", token, info); |
68 | |
69 | notifySuccess(token, info); |
70 | |
71 | return info; |
72 | } |
代码很多,有用的只有 doAuthencate(token) 这句。
2. ModularRealmAuthenticator | doAuthencate(token)
1 | // line 164 |
2 | /** |
3 | * Performs the authentication attempt by interacting with the single configured realm, which is significantly |
4 | * simpler than performing multi-realm logic. |
5 | * |
6 | * @param realm the realm to consult for AuthenticationInfo. |
7 | * @param token the submitted AuthenticationToken representing the subject's (user's) log-in principals and credentials. |
8 | * @return the AuthenticationInfo associated with the user account corresponding to the specified {@code token} |
9 | */ |
10 | protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) { |
11 | if (!realm.supports(token)) { |
12 | String msg = "Realm [" + realm + "] does not support authentication token [" + |
13 | token + "]. Please ensure that the appropriate Realm implementation is " + |
14 | "configured correctly or that the realm accepts AuthenticationTokens of this type."; |
15 | throw new UnsupportedTokenException(msg); |
16 | } |
17 | // 使用 realm.getAuthenticationInfo(token) 校验并获取 authenticationInfo |
18 | AuthenticationInfo info = realm.getAuthenticationInfo(token); |
19 | if (info == null) { |
20 | String msg = "Realm [" + realm + "] was unable to find account data for the " + |
21 | "submitted AuthenticationToken [" + token + "]."; |
22 | throw new UnknownAccountException(msg); |
23 | } |
24 | return info; |
25 | } |
26 | |
27 | /** |
28 | * Performs the multi-realm authentication attempt by calling back to a {@link AuthenticationStrategy} object |
29 | * as each realm is consulted for {@code AuthenticationInfo} for the specified {@code token}. |
30 | * |
31 | * @param realms the multiple realms configured on this Authenticator instance. |
32 | * @param token the submitted AuthenticationToken representing the subject's (user's) log-in principals and credentials. |
33 | * @return an aggregated AuthenticationInfo instance representing account data across all the successfully |
34 | * consulted realms. |
35 | */ |
36 | protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { |
37 | |
38 | AuthenticationStrategy strategy = getAuthenticationStrategy(); |
39 | |
40 | AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token); |
41 | |
42 | if (log.isTraceEnabled()) { |
43 | log.trace("Iterating through {} realms for PAM authentication", realms.size()); |
44 | } |
45 | |
46 | for (Realm realm : realms) { |
47 | |
48 | aggregate = strategy.beforeAttempt(realm, token, aggregate); |
49 | |
50 | if (realm.supports(token)) { |
51 | |
52 | log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm); |
53 | |
54 | AuthenticationInfo info = null; |
55 | Throwable t = null; |
56 | try { |
57 | info = realm.getAuthenticationInfo(token); |
58 | } catch (Throwable throwable) { |
59 | t = throwable; |
60 | if (log.isDebugEnabled()) { |
61 | String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:"; |
62 | log.debug(msg, t); |
63 | } |
64 | } |
65 | |
66 | aggregate = strategy.afterAttempt(realm, token, info, aggregate, t); |
67 | |
68 | } else { |
69 | log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token); |
70 | } |
71 | } |
72 | |
73 | aggregate = strategy.afterAllAttempts(token, aggregate); |
74 | |
75 | return aggregate; |
76 | } |
77 | |
78 | |
79 | /** |
80 | * Attempts to authenticate the given token by iterating over the internal collection of |
81 | * {@link Realm}s. For each realm, first the {@link Realm#supports(org.apache.shiro.authc.AuthenticationToken)} |
82 | * method will be called to determine if the realm supports the {@code authenticationToken} method argument. |
83 | * <p/> |
84 | * If a realm does support |
85 | * the token, its {@link Realm#getAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)} |
86 | * method will be called. If the realm returns a non-null account, the token will be |
87 | * considered authenticated for that realm and the account data recorded. If the realm returns {@code null}, |
88 | * the next realm will be consulted. If no realms support the token or all supporting realms return null, |
89 | * an {@link AuthenticationException} will be thrown to indicate that the user could not be authenticated. |
90 | * <p/> |
91 | * After all realms have been consulted, the information from each realm is aggregated into a single |
92 | * {@link AuthenticationInfo} object and returned. |
93 | * |
94 | * @param authenticationToken the token containing the authentication principal and credentials for the |
95 | * user being authenticated. |
96 | * @return account information attributed to the authenticated user. |
97 | * @throws IllegalStateException if no realms have been configured at the time this method is invoked |
98 | * @throws AuthenticationException if the user could not be authenticated or the user is denied authentication |
99 | * for the given principal and credentials. |
100 | */ |
101 | protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException { |
102 | // 判断是否配置了realms,否的话抛出异常 |
103 | assertRealmsConfigured(); |
104 | Collection<Realm> realms = getRealms(); |
105 | // 判断是单realm还是多realm,分别调用不同的方法处理 |
106 | if (realms.size() == 1) { |
107 | return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken); |
108 | } else { |
109 | return doMultiRealmAuthentication(realms, authenticationToken); |
110 | } |
111 | } |
先来看 doSingleRealmAuthentication(realm),它内部直接调用了 realm.getAuthenticationInfo(token)
3. AbstractAuthenticator | notifySuccess()
1 | /** |
2 | * Notifies any registered {@link AuthenticationListener AuthenticationListener}s that |
3 | * authentication was successful for the specified {@code token} which resulted in the specified |
4 | * {@code info}. This implementation merely iterates over the internal {@code listeners} collection and |
5 | * calls {@link AuthenticationListener#onSuccess(AuthenticationToken, AuthenticationInfo) onSuccess} |
6 | * for each. |
7 | * |
8 | * @param token the submitted {@code AuthenticationToken} that resulted in a successful authentication. |
9 | * @param info the returned {@code AuthenticationInfo} resulting from the successful authentication. |
10 | */ |
11 | protected void notifySuccess(AuthenticationToken token, AuthenticationInfo info) { |
12 | for (AuthenticationListener listener : this.listeners) { |
13 | listener.onSuccess(token, info); |
14 | } |
15 | } |
小结
- 如果是单 realm ,则直接调用 realm 的 getAuthenticationInfo()
- 如果是多 realm ,则使用 AuthenticationStrategy 对象来调用 realm 的 getAuthenticationInfo() 方法


































