Shiro getAuthenticationInfo()中抛出异常, 全局异常处理却收不到自己抛出的异常中, 介绍了通过覆盖AuthenticationStrategy的afterAttempt方法, 更改Shiro的异常抛出逻辑.
本文介绍AuthenticationStrategy在验证过程中到底做了什么
想要弄清Strategy, 就要关注在ModularRealmAuthenticator.doMultiRealmAuthentication()的aggregate
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms,
AuthenticationToken token) {
// 获取验证策略
AuthenticationStrategy strategy = getAuthenticationStrategy();
**AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);**
// 循环遍历realm
for (Realm realm : realms) {
//
try {
**aggregate = strategy.beforeAttempt(realm, token, aggregate);**
} catch (ShortCircuitIterationException shortCircuitSignal) {
break;
}
if (realm.supports(token)) {
AuthenticationInfo info = null;
Throwable t = null;
try {
**// 这里调用了自定义Realm的getAuthenticationInfo方法**
**info = realm.getAuthenticationInfo(token);**
} catch (Throwable throwable) {... }
**aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);**
} else {...}
}
**aggregate = strategy.afterAllAttempts(token, aggregate);**
return aggregate;
首先, 根据字面意思, aggregate释义"集合, 集合体".
其次, 在上述方法的执行过程中, aggregate起到了将从realm中返回的认证信息(即AuthenticationInfo)组成一个大集合的作用.
上述代码块中, 标注黑体的地方就是Strategy发挥作用的地方, 下面进行一一解析.
beforeAllAttempts 方法:
new并返回了一个 空的SimpleAuthenticationInfo
public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException {
return new SimpleAuthenticationInfo();
}
beforeAttemps方法:
看似传了一大波参数, 实际上就是将aggregate原封不动地返回
public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException {
return aggregate;
}
afterAttempt方法,
传入了当前Realm, 在Realm中抛出的异常, 以及两个AuthenticationInfo.
两个Info解释: 假设目前已经有n个Realm执行, 那么aggregateInfo就是前n-1个Realm中返回Info的聚合, singleRealmInfo 则是第n个Realm返回的Info.
@Override
public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
if (t != null && t instanceof AuthenticationException){
throw (AuthenticationException) t;
}
return super.afterAttempt(realm, token, singleRealmInfo, aggregateInfo, t);
}
继续查看父类中的afterAttemp:
public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
AuthenticationInfo info;
if (singleRealmInfo == null) { // 如果当前Info为null, 那就直接不聚合该Info
info = aggregateInfo;
} else { // 否则对该Info进行聚合
if (aggregateInfo == null) {
info = singleRealmInfo;// 如果之前聚合过的Info为空, 那么直接设当前Info为聚合Info
} else {
info = merge(singleRealmInfo, aggregateInfo); // 否则将两个info进行聚合
}
}
到这里, 大概可以明白: afterAttempt方法的作用就是: 对Realm中的异常进行处理, 聚合新的Info.
public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
AuthenticationInfo info;
if (singleRealmInfo == null) {
info = aggregateInfo;
} else {
if (aggregateInfo == null) {
info = singleRealmInfo;
} else {
info = merge(singleRealmInfo, aggregateInfo);
}
}
return info;
}
afterAllAttempts 方法 :
判断aggregate是否为null或Empty, ...
说白了就是: 处理当aggregate异常的状况
public AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException {
if (aggregate == null ||isEmpty(aggregate.getPrincipals())) {
throw new AuthenticationException("...");
}
return aggregate;
}
当且仅当上述四个方法执行过程中没有抛出任何异常, doMultiRealmAuthentication()方法才能将最终聚合的Info成功返回!
在实际的开发中, 当然可以通过对这些方法进行自定义, 实现自己想要的逻辑! 具体流程的话, 可以看看: Shiro getAuthenticationInfo()中抛出异常, 全局异常处理却收不到自己抛出的异常