nStep3:SubjectManager 接收token 以及简单地委托给内部的Authenticator 实例通过调用authenticator.authenticate(token)。这通常是一个ModularRealmAuthenticator 实例,支持在身份验证中协调一个或多个Realm 实例。
n
nStep 4:如果应用程序中配置了一个以上的Realm,ModularRealmAuthenticator 实例将利用配置好的AuthenticationStrategy 来启动Multi-Realm 认证尝试。在Realms 被身份验证调用之前,期间和以后,AuthenticationStrategy 被调用使其能够对每个Realm 的结果作出反应。
n
nStep 5:每个配置的Realm 用来帮助看它是否支持提交的AuthenticationToken。如果支持,那么支持Realm 的getAuthenticationInfo 方法将会伴随着提交的token 被调用。getAuthenticationInfo 方法有效地代表一个特定Realm 的单一的身份验证尝试。
初识自定义
Realm
n这里先来个例子,认识一下:
public class MyRealm extends AuthorizingRealm{
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName = (String) getAvailablePrincipal(principals);
//通过用户名去获得用户的所有资源,并把资源存入info中
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Set s = new HashSet();
s.add("p1"); s.add("p2"); info.setStringPermissions(s);
Set r = new HashSet();
r.add("r1"); r.add("r2"); info.setRoles(r);
return info;}
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
//token中储存着输入的用户名和密码
UsernamePasswordToken upToken = (UsernamePasswordToken)token;
String username = upToken.getUsername();
String password = String.valueOf(upToken.getPassword());
//通常是与数据库中用户名和密码进行比对,这里就省略了
//比对成功则返回info,比对失败则抛出对应信息的异常AuthenticationException
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password .toCharArray(),getName());
return info; }}
配置多个Realm
n上面的例子可以作为第一个Realm
n再复制一份,定义为MyRealm2,在返回user前添加抛出一个例外,表示认真没有通过,如下:
if(username.equals("javass")){
throw new AuthenticationException("MyRealm2 认证失败");
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password .toCharArray(),getName());
n在配置文件里面添加Realm的定义
myRealm1=cn.javass.hello.MyRealm
myRealm2=cn.javass.hello.MyRealm2
由于有多个realm,一般就需要配置AuthenticationStrategy了,而AuthenticationStrategy是跟Authenticator(认证器)相关的。
n配置Authenticator和AuthenticationStrategy
authenticator = org.apache.shiro.authc.pam.ModularRealmAuthenticator
authcStrategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
authenticator.authenticationStrategy = $authcStrategy
authenticator.realms=$myRealm2,$myRealm1
n当然,你可以扩展并实现自己的Authenticator,一般没有必要
n最后把Authenticator设置给securityManager
securityManager.authenticator = $authenticator
n关于AuthenticationStrategy的配置,有三种:
AtLeastOneSuccessfulStrategy :如果一个(或更多)Realm 验证成功,则整体的尝试被认为是成功的。如果没有一个验证成功,则整体尝试失败。
FirstSuccessfulStrategy 只有第一个成功地验证的Realm 返回的信息将被使用。所有进一步的Realm 将被忽略。如果没有一个验证成功,则整体尝试失败
AllSucessfulStrategy 为了整体的尝试成功,所有配置的Realm 必须验证成功。如果没有一个验证成功,则整体尝试失败。
ModularRealmAuthenticator 默认的是AtLeastOneSuccessfulStrategy
n自定义自己的AuthenticationStrategy,通常是扩展自AbstractAuthenticationStrategy,示例如下:
public class MyAuthenticationStrategy extends AbstractAuthenticationStrategy{
public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
if(realm.getName().equals("myRealm2")){
if(singleRealmInfo==null || singleRealmInfo.getPrincipals()==null){
throw new AuthenticationException("主战认证未通过");
}
}
return super.afterAttempt(realm, token, singleRealmInfo, aggregateInfo, t);
}
}
至于具体覆盖扩展什么方法,需要根据你具体的策略来定。
多个Realm的验证顺序
n概述
非常重要的一点是:ModularRealmAuthenticator 将与Realm 实例以迭代的顺序进行交互。
在SecurityManager 中已经配置好了ModularRealmAuthenticator 对Realm实例的访问。当执行一个认证尝试时,它将会遍历该集合,并对每一个支持提交AuthenticationToken 的Realm 调用Realm 的getAuthenticationInfo 方法
n隐式排列
当你配置多个realm的时候,处理的顺序默认就是你配置的顺序。
这种情况通常就是只定义了realm,而没有配置securityManager的realms
n显示排列
也就是显示的配置securityManager.realms,那么执行的顺序就是你配置该值的realm的顺序。
通常更推荐显示排列。