作为一个优秀的安全框架,你不止可以使用它内部提供的强大功能,还可以根据需要去实现接口定制自己需要的功能,前面我们使用IniReam和JdbcRealm作为资源去获取用户和授权信息。但是我们不得不按照Shiro提供的用户和权限格式去做,比如JdbcRealm假如我们有更加复杂的表结构,则使用JdbcRealm则可能显得极为复杂,此时,我们就可以自定义Realm去获取相关数据源,该篇除了介绍自定义Realm,我们还会介绍认证策略。
自定义Realm
前面使用了IniRealm和JdbcRealm,我们通过工具可以知道他们继承关系,如下图所示:
上面的类图继承关系中,Realm为根接口, 它是一个安全组件去控制安全相关的实体(用户,角色,权限)等的认证或者授权操作的访问。它有三个方法:String getName()获取Realm实现类的名称;boolean supports(AuthenticationToken token)验证是否支持AuthenticationToken 实例,AuthenticationInfo getAuthenticationInfo(AuthenticationToken token)返回一个具体的账户信息,如果获取不到则返回空,该方法是自定义Realm的根本。CacheRealm增加了缓存相关功能,AuthenticatingRealm则是添加了了认证的相关功能,AuthoringReam则是添加了授权的功能。所有的自定义Realm需要集成AuthoringReam。如下代码详解:
public class CustomizedRealm extends AuthorizingRealm {
//该方法将用户的角色和权限信息封装成AuthorizationInfo实例,交给Shiro管理
//后续的所有权限问题,都可以由Shiro进行控制
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Set<String> roles = new HashSet<String>();
//可以从数据库查询角色,根据PrincipalCollection实例中的用户信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roles);
//可以从数据库查询权限,根据PrincipalCollection实例中的用户信息
Set<String> permissions = new HashSet<String>();
info.setStringPermissions(permissions);
return info;
}
//该方法将用户认证信息封装成AuthenticationInfo实例,如果账号信息不存在,则返回null
//认证的后续操作也是由Shiro来完成
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upt = (UsernamePasswordToken) token;
String userName = upt.getUsername();
String password = String.valueOf(upt.getPassword());
//可以从数据库查询用户信息,存在就封装成AuthenticationInfo 实例,不存在则返回空即可
if("pharos".equals(userName) && "333".equals(password)) {
AuthenticationInfo info = new SimpleAccount(userName, password, getName());
return info;
}
return null;
}
}
自定义Realm很简单,就是继承AuthoringReam,然后自己实现doGetAuthorizationInfo和doGetAuthenticationInfo方法将用户信息和角色权限信息分别封装成AuthenticationInfo和AuthorizationInfo。
认证策略
Shiro可以支持多个Realm做认证和授权,在单一的Realm中ModularRealmAuthenticator直接调用这个单一的Realm,如果有两个或两个以上的 Realm 配置,它将使用 AuthenticationStrategy 实例来调整这些尝试如何出现。下面我们将介绍Shiro的认证策略:
AuthenticationStrategies。Shiro中定义了三种具体的AuthenticationStrategies实现:
策略 | 描述 |
AtLeastOneSuccessfulStrategy
|
如果一个(或更多)
Realm
验证成功,则整体的尝试被认为是成功的。如果没有一个验证成功,
则整体尝试失败。
|
FirstSuccessfulStrategy
|
只有第一个成功地验证的
Realm
返回的信息将被使用。所有进一步的
Realm
将被忽略。如果没有
一个验证成功,则整体尝试失败。
|
AllSucessfulStrategy
|
为了整体的尝试成功,所有配置的
Realm
必须验证成功。如果没有一个验证成功,则整体尝试失
败
|
[main]
# 配置两个Realm
dataSource = com.alibaba.druid.pool.DruidDataSource
dataSource.url = jdbc:mysql://localhost:3306/shiro
dataSource.username= root
dataSource.password = 123456
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $dataSource
customizedRealm = cn.org.microservice.strategy.realm.CustomizedRealm
securityManager.realms=$customizedRealm,$jdbcRealm
#配置策略
strategy = org.apache.shiro.authc.pam.AllSuccessfulStrategy
securityManager.authenticator.authenticationStrategy = $strategy
自定义策略需要继承AbstractAuthenticationStrategy类,具体的实现可以参考Shiro提供的三个策略。