该解决思路来自StackOverflow: Apache Shiro: Exception-Handling with Multiple Realms
首先下结论: 这是因为使用了默认的AuthenticationStrategy所致, 只需要为自己的shiro实现自定义的AuthenticationStrategy, 并将其正确配置即可.
public class MyAtLeastOneSuccessfulStrategy extends AtLeastOneSuccessfulStrategy {
@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);
}
}
配置AuthenticationStrategy的方法如下:
MyAtLeastOneSuccessfulStrategy strategy = new MyAtLeastOneSuccessfulStrategy();
((ModularRealmAuthenticator)(securityManager.getAuthenticator()))
.setAuthenticationStrategy(strategy);
如果希望知道为何收到的是AuthenticationException, 以及为何要按照上面这么配置. 请看源码分析(我的经历):
为何总是收不到自己throw的异常
doGetAuthenticationInfo方法中
if (user == null)
throw new UnknownAccountException("账户不存在!");
在调用一堆构造函数后, 进入ModularRealmAuthenticator的doMultiRealmAuthentication方法
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;
}
if (realm.supports(token)) {
log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);
AuthenticationInfo info = null;
Throwable t = null;
try {
**// 这里调用了自定义Realm的getAuthenticationInfo方法**
**info = realm.getAuthenticationInfo(token);**
} catch (Throwable throwable) {
**// 这里采用catch将Realm中抛出的异常直接捕获,
// 并且在后续的处理中并没有将该异常重新抛出**
t = throwable;
if (log.isDebugEnabled()) {
String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
log.debug(msg, t);
}
}
// 如果在Realm中抛出异常,