Authenticator在shiro中是主要负责验证用户登录,它通常和一组Realm来协调、交互验证用户登录。一般情况下我们不会直接使用Authenticator。而是交由SecurityManager来委托使用。
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);
}
}
}
}
}