5.Authenticator(登录验证器)

Authenticator在shiro中是主要负责验证用户登录,它通常和一组Realm来协调、交互验证用户登录。一般情况下我们不会直接使用Authenticator。而是交由SecurityManager来委托使用。
5.Authenticator(登录验证器)_第1张图片
1.Authenticator

public interface Authenticator {

    //根据用户输入的AuthenticationToken(账号密码)然后去和底层数据库进行匹配验证登录。如果成功则返回AuthenticationInfo
    //(AuthenticationToken在数据库中对应的用户信息),失败则抛出异常
    public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException;
}

2.AbstractAuthenticator

//Authenticator的顶层抽象类,该类没有实现具体的用户登录验证逻辑,只是提供了一些通知功能。如登录成功、登录失败或者退出时候,
//则迭代调用AuthenticationListener的onSuccess、onFailure、onLogout方法执行用户自定义的逻辑操作
public abstract class AbstractAuthenticator implements Authenticator, LogoutAware {


    private static final Logger log = LoggerFactory.getLogger(AbstractAuthenticator.class);

    //用户注册的AuthenticationListener(里面有onSuccess、onFailure、onLogout方法)
    private Collection<AuthenticationListener> listeners;

    /*------------------------------------------- | C O N S T R U C T O R S | ============================================*/


    public AbstractAuthenticator() {
        listeners = new ArrayList<AuthenticationListener>();
    }

    /*-------------------------------------------- | A C C E S S O R S / M O D I F I E R S | ============================================*/


    @SuppressWarnings({"UnusedDeclaration"})
    public void setAuthenticationListeners(Collection<AuthenticationListener> listeners) {
        if (listeners == null) {
            this.listeners = new ArrayList<AuthenticationListener>();
        } else {
            this.listeners = listeners;
        }
    }


    @SuppressWarnings({"UnusedDeclaration"})
    public Collection<AuthenticationListener> getAuthenticationListeners() {
        return this.listeners;
    }

    /*------------------------------------------- | M E T H O D S | ============================================*/

    //当用户登录验证成功时候,调用此方法迭代AuthenticationListener的onSuccess方法
    protected void notifySuccess(AuthenticationToken token, AuthenticationInfo info) {
        for (AuthenticationListener listener : this.listeners) {
            listener.onSuccess(token, info);
        }
    }

    //当用户登录验证失败时候,调用此方法迭代AuthenticationListener的onFailure方法
    protected void notifyFailure(AuthenticationToken token, AuthenticationException ae) {
        for (AuthenticationListener listener : this.listeners) {
            listener.onFailure(token, ae);
        }
    }

    //当用户退出时候,调用此方法迭代AuthenticationListener的onLogout方法
    protected void notifyLogout(PrincipalCollection principals) {
        for (AuthenticationListener listener : this.listeners) {
            listener.onLogout(principals);
        }
    }

    //用户退出时候执行
    public void onLogout(PrincipalCollection principals) {
        notifyLogout(principals);
    }

    //实现Authenticator的authenticate方法,验证用户登录
    public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {

        if (token == null) {
            throw new IllegalArgumentException("Method argumet (authentication token) cannot be null.");
        }

        log.trace("Authentication attempt received for token [{}]", token);

        AuthenticationInfo info;
        try {
            //调用子类实现的模板方法,执行真正的验证逻辑
            info = doAuthenticate(token);
            if (info == null) {
                String msg = "No account information found for authentication token [" + token + "] by this " +
                        "Authenticator instance. Please check that it is configured correctly.";
                throw new AuthenticationException(msg);
            }
        } catch (Throwable t) {
            AuthenticationException ae = null;
            //包装异常
            if (t instanceof AuthenticationException) {
                ae = (AuthenticationException) t;
            }
            if (ae == null) {
                //Exception thrown was not an expected AuthenticationException. Therefore it is probably a little more
                //severe or unexpected. So, wrap in an AuthenticationException, log to warn, and propagate:
                String msg = "Authentication failed for token submission [" + token + "]. Possible unexpected " +
                        "error? (Typical or expected login exceptions should extend from AuthenticationException).";
                ae = new AuthenticationException(msg, t);
            }
            try {
                //验证失败,调用失败通知
                notifyFailure(token, ae);
            } catch (Throwable t2) {
                if (log.isWarnEnabled()) {
                    String msg = "Unable to send notification for failed authentication attempt - listener error?. " +
                            "Please check your AuthenticationListener implementation(s). Logging sending exception " +
                            "and propagating original AuthenticationException instead...";
                    log.warn(msg, t2);
                }
            }


            throw ae;
        }

        log.debug("Authentication successful for token [{}]. Returned account [{}]", token, info);
        //执行到这里,代表用户验证成功,则调用验证成功通知
        notifySuccess(token, info);

        return info;
    }

    //模板方法,子类实现的真正的验证逻辑
    protected abstract AuthenticationInfo doAuthenticate(AuthenticationToken token)
            throws AuthenticationException;


}

3.ModularRealmAuthenticator

/** PAM(Pluggable Authentication Module):PAM是一个聚合底层的验证服务模块的高级API 每一个底层的验证服务模块都可以作为一个验证方案,可根据需求来配置集合成一个验证 服务。 ModularRealmAuthenticator继承于AbstractAuthenticator,也属于PAM里的一个验证服务模块,用户可根据 需求来自定义Realm,然后插入到ModularRealmAuthenticator的realms。 如果用户配置多个Realm时,用户可配置Realm的使用策略,如:至少一个Realm验证成功(AtLeastOneSuccessfulStrategy), 或者必须所有的Realm都成功(AllSuccessfulStrategy),来决定后续的逻辑操作。 **/
public class ModularRealmAuthenticator extends AbstractAuthenticator {


    private static final Logger log = LoggerFactory.getLogger(ModularRealmAuthenticator.class);

    //Realm集合,用于用户登陆验证时候
    private Collection<Realm> realms;

    //配置多个Realm使用策略
    private AuthenticationStrategy authenticationStrategy;

    /*-------------------------------------------- | C O N S T R U C T O R S | ============================================*/


    public ModularRealmAuthenticator() {
        //默认多个Realm使用策略:AtLeastOneSuccessfulStrategy(至少一个成功)
        this.authenticationStrategy = new AtLeastOneSuccessfulStrategy();
    }

    //设置Realms,提供PAM配置支持
    public void setRealms(Collection<Realm> realms) {
        this.realms = realms;
    }


    protected Collection<Realm> getRealms() {
        return this.realms;
    }



    public AuthenticationStrategy getAuthenticationStrategy() {
        return authenticationStrategy;
    }

    //覆盖默认多Realm使用策略
    public void setAuthenticationStrategy(AuthenticationStrategy authenticationStrategy) {
        this.authenticationStrategy = authenticationStrategy;
    }

    //确保Realms不为空,至少需要一个,否则抛异常。
    protected void assertRealmsConfigured() throws IllegalStateException {
        Collection<Realm> realms = getRealms();
        if (CollectionUtils.isEmpty(realms)) {
            String msg = "Configuration error: No realms have been configured! One or more realms must be " +
                    "present to execute an authentication attempt.";
            throw new IllegalStateException(msg);
        }
    }

    //单个Realm执行的验证逻辑
    protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
        //首先判断Realm是否支持该token
        if (!realm.supports(token)) {
            String msg = "Realm [" + realm + "] does not support authentication token [" +
                    token + "]. Please ensure that the appropriate Realm implementation is " +
                    "configured correctly or that the realm accepts AuthenticationTokens of this type.";
            throw new UnsupportedTokenException(msg);
        }
    //realm调用getAuthenticationInfo(token)来执行真正的数据验证交互。该方法如果验证失败会抛出相对应异常
        AuthenticationInfo info = realm.getAuthenticationInfo(token);
        if (info == null) {
            String msg = "Realm [" + realm + "] was unable to find account data for the " +
                    "submitted AuthenticationToken [" + token + "].";
            throw new UnknownAccountException(msg);
        }
        return info;
    }

    //执行多个Realm验证逻辑
    protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
        //获取多个Realm使用策略
        AuthenticationStrategy strategy = getAuthenticationStrategy();

    /** 以下的AuthenticationStrategy的方法我只针对AtLeastOneSuccessfulStrategy来说!!! **/

        //执行所有Realm之前,AuthenticationStrategy首先初始化个SimpleAuthenticationInfo
        AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);

        if (log.isTraceEnabled()) {
            log.trace("Iterating through {} realms for PAM authentication", realms.size());
        }

        for (Realm realm : realms) {
        //执行单个Realm之前,直接返回aggregate
            aggregate = strategy.beforeAttempt(realm, token, aggregate);
            //Realm先判断是否支持该token
            if (realm.supports(token)) {

                log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);

                AuthenticationInfo info = null;
                Throwable t = null;
                try {
            //realm调用真正的验证登陆处理逻辑
                    info = realm.getAuthenticationInfo(token);
                } catch (Throwable throwable) {
                    t = throwable;
                    if (log.isDebugEnabled()) {
                        String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
                        log.debug(msg, t);
                    }
                }
                //单个Realm执行之后,合并info和aggregate
                aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);

            } else {
                log.debug("Realm [{}] does not support token {}. Skipping realm.", realm, token);
            }
        }
        //所有Realm执行之后,判断是否aggregate的getPrincipals是否为空,因为至少需要一个验证成功,所以为空则抛出异常
        aggregate = strategy.afterAllAttempts(token, aggregate);
        //执行到这里,至少有一个Realm登陆验证成功。
        return aggregate;
    }


    //实现Authenticator的authenticate方法
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        //首先验证realm是否为空
        assertRealmsConfigured();
        Collection<Realm> realms = getRealms();
        if (realms.size() == 1) {
        //单个realm则执行单realm逻辑处理
            return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
        } else {
        //处理多realm逻辑处理(依据AuthenticationStrategy)
            return doMultiRealmAuthentication(realms, authenticationToken);
        }
    }

    //退出时候调用
    public void onLogout(PrincipalCollection principals) {
    //首先调用父类的onLogout,调用退出通知
        super.onLogout(principals);
        Collection<Realm> realms = getRealms();
        if (!CollectionUtils.isEmpty(realms)) {
            for (Realm realm : realms) {
            //迭代Realm执行退出方法,主要是清理缓存
                if (realm instanceof LogoutAware) {
                    ((LogoutAware) realm).onLogout(principals);
                }
            }
        }
    }
}

你可能感兴趣的:(shiro,登陆验证)