学习 准备 尝试 谨慎小心

0%

Shiro源码解析之Authenticator

了解 Authenticator

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

ModularRealmAuthenticator

理解 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
    }

小结

  1. 如果是单 realm ,则直接调用 realm 的 getAuthenticationInfo()
  2. 如果是多 realm ,则使用 AuthenticationStrategy 对象来调用 realm 的 getAuthenticationInfo() 方法