shiro身份认证的策略分析
1.Authenticator 及 AuthenticationStrategy
Authenticator 的职责是验证用户帐号,是 Shiro API 中身份验证核心的入口点:
public AuthenticationInfo authenticate(AuthenticationToken authenticationToken)
throws AuthenticationException;
如果验证成功,将返回 AuthenticationInfo 验证信息;此信息中包含了身份及凭证;如果验证失败将抛出相应的 AuthenticationException 实现。
1.SecurityManager 接口继承了 Authenticator默认实现为ModularRealmAuthenticator
ModularRealmAuthenticator 实现,其委托给多个 Realm 进行验证,验证规则通过 AuthenticationStrategy 接口指定,默认使用AtLeastOneSuccessfulStrategy 策略。
2.三个验证策略
FirstSuccessfulStrategy:只要有一个 Realm 验证成功即可,只返回第一个 Realm 身份验证成功的认证信息,其他的忽略;
AtLeastOneSuccessfulStrategy:只要有一个 Realm 验证成功即可,和 FirstSuccessfulStrategy 不同,返回所有 Realm 身份验证成功的认证信息;
AllSuccessfulStrategy:所有 Realm 验证成功才算成功,且返回所有 Realm 身份验证成功的认证信息,如果有一个失败就失败了。
2.配置实现策略
2.1使用FirstSuccessfulStrategy策略
shiro.ini文件
[main]
#指定securityManager的authenticator实现
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
#指定securityManager.authenticator的authenticationStrategy
firstSuccessfulStrategy=org.apache.shiro.authc.pam.FirstSuccessfulStrategy
securityManager.authenticator.authenticationStrategy=firstSuccessfulStrategy
myRealm1=com.github.shiro.realm.MyRealm1
myRealm2=com.github.shiro.realm.MyRealm2
myRealm3=com.github.shiro.realm.MyRealm3
securityManager.realms=$myRealm1,$myRealm2,$myRealm3
2.2使用AtLeastOneSuccessfulStrategy策略
同上,只修改securityManager.authenticator的authenticationStrategy
2.3使用AllSuccessfulStrategy策略
同上,只修改securityManager.authenticator的authenticationStrategy
3.自定义身份认证策略
自定义 AuthenticationStrategy 实现,首先看其 API:
//在所有Realm验证之前调用
AuthenticationInfo beforeAllAttempts(
Collection<? extends Realm> realms, AuthenticationToken token)
throws AuthenticationException;
//在每个Realm之前调用
AuthenticationInfo beforeAttempt(
Realm realm, AuthenticationToken token, AuthenticationInfo aggregate)
throws AuthenticationException;
//在每个Realm之后调用
AuthenticationInfo afterAttempt(
Realm realm, AuthenticationToken token,
AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t)
throws AuthenticationException;
//在所有Realm之后调用
AuthenticationInfo afterAllAttempts(
AuthenticationToken token, AuthenticationInfo aggregate)
throws AuthenticationException;
3.1实现过程
1)实现至少两认证成功
- 继承AbstractAuthenticationStrategy 并
重写方法afterAllAttempts
public class AtLeastTwoAuthenticatorStrategy extends AbstractAuthenticationStrategy {
@Override
public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException {
return new SimpleAuthenticationInfo();//返回一个权限的认证信息
}
@Override
public AuthenticationInfo beforeAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException {
return aggregate;//返回之前合并的
}
@Override
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;
}
@Override
public AuthenticationInfo afterAllAttempts(AuthenticationToken token, AuthenticationInfo aggregate) throws AuthenticationException {
if (aggregate == null || CollectionUtils.isEmpty(aggregate.getPrincipals()) || aggregate.getPrincipals().getRealmNames().size() < 2) {
throw new AuthenticationException("Authentication token of type [" + token.getClass() + "] " +
"could not be authenticated by any configured realms. Please ensure that at least two realm can " +
"authenticate these tokens.");
}
return aggregate;
}
}
2.修改shiro.ini文件
#指定securityManager.authenticator的authenticationStrategy
atLeastTwoAuthenticatorStrategy=com.github.shiro.authenticator.strategy.AtLeastTwoAuthenticatorStrategy
securityManager.authenticator.authenticationStrategy=atLeastTwoAuthenticatorStrategy