前言
最近看关于Shiro的一些文章,看的有点一头雾水。看了看源码,又看了看文章,把自己的理解的记录一下。
(看的文章:跟我学Shiro目录贴 。有兴趣的可以看一下,这里的例子来自于这些文章)
正文
Realm能做的工作主要有以下几个方面:
- 验证是否能登录,并返回验证信息(getAuthenticationInfo方法)
- 验证是否有访问指定资源的权限,并返回所拥有的所有权限(getAuthorizationInfo方法)
- 判断是否支持token(例如:HostAuthenticationToken,UsernamePasswordToken等)(supports方法)
这里主来说明一下关于前两点验证方面的逻辑。
1,验证是否能登录,并返回验证信息
首先先看一下Realm接口的内容,这个接口里有3个方法,内容如下:
- AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)
- boolean supports(AuthenticationToken token)
- String getName()
我们看到第一个方法就是我们上面说的“验证是否能登录,并返回验证信息”的方法。从方法的名字上看,只有取得验证信息的意思,其实这里面还包括了进行验证的逻辑。
看Javadoc,这个方法的作用是“根据传进来的Token,返回用户的验证信息”。下面说明一下“Token”和“用户验证信息”。
- Token:就是要拿来进行验证的信息,例如:如果是UsernamePasswordToken的话,这个Token的内容就是“用户提交的用户名和密码”。
- 用户验证信息:就是用户验证通过后,返回给系统的信息。例如:用户登录验证的话,一般来说,返回给系统的“用户验证信息”就应该是这个用户的“用户名和密码”。但也可以返回其它信息,例如返回用户的“邮箱地址和登录密码”信息,做为“用户验证信息”。
(具体什么情况下需要这么做还不知道,可能在多系统通过同一点进行登录,但由于历史原因,每个系统使用的用户信息不一致(有的使用uid,有的使用邮箱)时,使用吧)。
上面说了“根据传进来的Token”和“返回用户的验证信息”,但没有说验证的过程,这个过程也是在这个方法中进行。我们看一下源码:
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo info = getCachedAuthenticationInfo(token);
// doGetAuthenticationInfo方法的内容,由各个子类来实现。
// 主要是用来取得我们保存的“用户验证信息”,例如DB里保存的密码(具体看JdbcRealm的方法实现)
if (info == null) {
info = doGetAuthenticationInfo(token);
...
}
// 在这里,把用户提交的信息(Token)和我们保存的“用户验证信息”进行比较
// 如果不通过,直接抛出定义好的异常。
if (info != null) {
assertCredentialsMatch(token, info);
} else {
return info;
}
2,验证是否有访问指定资源的权限,并返回所拥有的所有权限
“验证是否登录”的处理,是由接口定义的。但“验证是否有访问权限”的逻辑,则是由类定义的。定义的类为:AuthorizingRealm
,在这个类中有个getAuthorizationInfo
方法。这个方法和getAuthenticationInfo
方法的处理流程有点像:
- 验证是否有指定的权限
- 返回用户的权限信息
同样,这个方法里面也包含了“验证”逻辑。
3,实现自定义Realm
从上面的功能说明可以看出来,在权限控制中比较重要的验证(登录或权限)逻辑,都是在Realm中做的。Realm的类继承如下:
继承了AuthorizingRealm的类,都要实现上面说的getAuthenticationInfo和getAuthorizationInfo方法,来完成登录和权限的验证。但如果自定义的Realm类只实现Realm接口的话,只需要getAuthenticationInfo方法就可以。下面看一个只实现Realm接口的自定义Realm。
public class MyRealm1 implements Realm {
@Override
public String getName() {
return "myrealm1";
}
@Override
public boolean supports(AuthenticationToken token) {
//仅支持UsernamePasswordToken类型的Token
return token instanceof UsernamePasswordToken;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String)token.getPrincipal(); //得到用户名
String password = new String((char[])token.getCredentials()); //得到密码
if(!"zhang".equals(username)) {
throw new UnknownAccountException(); //如果用户名错误
}
if(!"123".equals(password)) {
throw new IncorrectCredentialsException(); //如果密码错误
}
//如果身份认证验证成功,返回一个AuthenticationInfo实现;
return new SimpleAuthenticationInfo(username, password, getName());
}
}
在上面的自定义Realm中,我们可以看到getAuthenticationInfo方法的实现。在这里,把用户名和密码都写死了,只为了简单的呈现上面说的逻辑。登录验证通过后,必须要返回一个AuthenticationInfo类型的实例。
为什么要返回AuthenticationInfo
呢?框架里,最后把返回的AuthenticationInfo
保存到了SecurityManager里了,可能是为了以后在某些地方使用,还没有深入去看。