Authenticator

简介

负责应用程序中用户鉴权操作;

通常为应用程序配置一个主身份验证器,通过主身份验证器协调配置的一组Realm,实现可插拔认证行为;

核心代码

/**
  * 鉴权
  */
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken) throws AuthenticationException;

实现子类

实现类如下:

public interface Authenticator
    public interface SecurityManager extends Authenticator, Authorizer, SessionManager
    public abstract class AbstractAuthenticator implements Authenticator, LogoutAware
        public class ModularRealmAuthenticator extends AbstractAuthenticator
  • SecurityManager

简介

详见SecurityManager;

核心代码

  • AbstractAuthenticator

简介

模板模式实现了鉴权的算法框架,将真正鉴权尝试委托给子类实现;

支持登录成功/失败以及登出操作的通知,以便当这些条件发生时,进行定制化逻辑处理;

核心代码

// 鉴权监听器集合
private Collection<AuthenticationListener> listeners;

/**
  * 通知成功事件
  */
protected void notifySuccess(AuthenticationToken token, AuthenticationInfo info) {
    for (AuthenticationListener listener : this.listeners) {
        listener.onSuccess(token, info);
    }
}

/**
  * 通知失败事件
  */
protected void notifyFailure(AuthenticationToken token, AuthenticationException ae) {
    for (AuthenticationListener listener : this.listeners) {
        listener.onFailure(token, ae);
    }
}

/**
  * 通知登出事件
  */
protected void notifyLogout(PrincipalCollection principals) {
    for (AuthenticationListener listener : this.listeners) {
        listener.onLogout(principals);
    }
}

/**
  * 登出
  */
public void onLogout(PrincipalCollection principals) {
    // 通知监听器登出事件
    notifyLogout(principals);
}

/**
  * 鉴权
  * 算法模板,基于AuthenticationToken鉴权的算法框架
  */
public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {

    // 校验入参
    if (token == null) {
        throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
    }

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

    AuthenticationInfo info;
    try {
        // 根据提交的token匹配查找系统中存储的AuthenticationInfo,由子类实现算法细节
        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);
            if (log.isWarnEnabled())
                log.warn(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);

    // 返回AuthenticationInfo信息
    return info;
}

/**
  * 鉴权
  * 模板子方法,由子类实现算法细节
  */
protected abstract AuthenticationInfo doAuthenticate(AuthenticationToken token)
        throws AuthenticationException;
  • ModularRealmAuthenticator

简介

由Realm集合实现鉴权;

该类实现了可插拔认证行为,即通过Realm集合和AuthenticationStrategy实现灵活认证控制;

如果只有一个Realm,那么就等价于Realm.getAuthenticationInfo(AuthenticationToken);

如果有多个Realm,便遍历所有的Realm,根据AuthenticationStrategy决定执行策略:

        一个Realm验证成功;

        全部Realm验证成功;

        其他;

核心代码

// Realm集合,动态增删Realm是实现可插拔鉴权的基础
private Collection<Realm> realms;
// 鉴权策略,多Realm场景有效,和Realm集合一起实现鉴权
private AuthenticationStrategy authenticationStrategy;

/**
  * 构造方法
  */
public ModularRealmAuthenticator() {
    // 默认采用AtLeastOneSuccessfulStrategy
    this.authenticationStrategy = new AtLeastOneSuccessfulStrategy();
}

/**
  * 校验配置的Realm集合的有效性
  */
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) {
    if (!realm.supports(token)) {
        // 不支持该类型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);
    }
    // 根据提交的token匹配查找系统中存储的AuthenticationInfo
    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) {

    AuthenticationStrategy strategy = getAuthenticationStrategy();

    // 鉴权的预处理
    AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);

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

    // 遍历Realm集合
    for (Realm realm : realms) {

        try {
            // 本次鉴权的预处理
            aggregate = strategy.beforeAttempt(realm, token, aggregate);
        } catch (ShortCircuitIterationException shortCircuitSignal) {
            // Break from continuing with subsequnet realms on receiving 
            // short circuit signal from strategy
            break;
        }

        // 校验该Realm是否支持该类型token
        if (realm.supports(token)) {

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

            AuthenticationInfo info = null;
            Throwable t = null;
            try {
                // 根据提交的token匹配查找系统中存储的AuthenticationInfo
                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);
                }
            }

            // 本次鉴权的后处理
            aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);


        } else {
            log.debug("Realm [{}] does not support token {}.  Skipping realm.", realm, token);
        }
    }

    // 鉴权的后处理
    aggregate = strategy.afterAllAttempts(token, aggregate);

    // 鉴权的结果
    return aggregate;
}

/**
  * 鉴权
  */
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 校验Realm集合的有效性
    assertRealmsConfigured();
    Collection<Realm> realms = getRealms();
    if (realms.size() == 1) {
        // 单Realm
        return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken);
    } else {
        // 多Realm
        return doMultiRealmAuthentication(realms, authenticationToken);
    }
}

/**
  * 登出
  */
public void onLogout(PrincipalCollection principals) {
    // 调用父类,通知监听器登出事件
    super.onLogout(principals);
    Collection<Realm> realms = getRealms();
    if (!CollectionUtils.isEmpty(realms)) {
        // 遍历Realm集合
        for (Realm realm : realms) {
            if (realm instanceof LogoutAware) {
                // 执行登出处理
                ((LogoutAware) realm).onLogout(principals);
            }
        }
    }
}
上一篇:c#学习笔记 ——判断与循环


下一篇:Postman中binary传递文件后台怎么接收