一. 为什么要自定义Realm
前面,我们在配置 Shiro 的用户信息和权限数据的时候,都是从 shiro.ini 配置文件里面读取的,
这些数据能不能从我们本地的数据库中进行读取呢? 当然可以, 可以通过 JdbcRealm 进行读取,
但是通过 JdbcRealm进行读取时,数据库的表的名称,表的字段都必须固定,非常不利于扩展。
所以,我们需要自定义Realm.
自定义Realm 时,我们常常 使用 org.apache.shiro.realm.AuthorizingRealm 类。
让我们自定义的类, 继承 AuthorizingRealm 抽象类
public abstract class AuthorizingRealm extends AuthenticatingRealm implements Authorizer, Initializable, PermissionResolverAware, RolePermissionResolverAware{ ... protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection); protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken paramAuthenticationToken) ... }
AuthorizingRealm 有两个抽象方法, doGetAuthorizationInfo 和 doGetAuthenticationInfo
其中, doGetAuthorizationInfo 进行授权操作, doGetAuthenticationInfo 进行认证操作。
认证操作时,常常返回 AuthenticationInfo的子类 SimpleAuthenticationInfo
授权操作时, 常常返回 AuthorizationInfo的子类 simpleAuthorizationInfo
关于Shiro 自定义 Realm获取数据的使用方式,我们详细了解一下。
二. 自定义Realm 获取虚拟的认证授权数据
二.一 创建自定义Realm,继承 AuthorizingRealm 接口
MyRealm
package com.yjl.customer; public class MyRealm extends AuthorizingRealm{ @Override public String getName() { return "MyRealm"; } //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) { return null; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken paramAuthenticationToken) throws AuthenticationException { return null; } }
二.二 创建配置文件 customer.ini,用于注入自定义的Realm
[main] #配置自定义realm myRealm=com.yjl.customer.MyRealm #注入自定义的realm securityManager.realm=$myRealm
二.三 在MyRealm 文件中编写认证的操作
//认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken paramAuthenticationToken) throws AuthenticationException { System.out.println("进入认证"); //获取要登录的用户信息 //UsernamePasswordToken token=(UsernamePasswordToken)paramAuthenticationToken; //获取用户名 //String userName=token.getUsername(); //也可以通过获取身份信息,然后转成 String 类型 String userName=(String)paramAuthenticationToken.getPrincipal(); //从数据库里面查询该用户的密码,省略数据库查询,直接固化下来,该用户的密码就是 1234 String password="1234"; SimpleAuthenticationInfo simpleAuthenticationInfo=new SimpleAuthenticationInfo(userName, password, getName()); return simpleAuthenticationInfo; }
SimpleAuthenticationInfo 对象里面放置三个参数, 身份,证明 和 getName()
二.四 在MyRealm 文件中编写授权的操作
//授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) { //获取凭证 用户名 System.out.println("进入授权"); String userName=(String)(paramPrincipalCollection.getPrimaryPrincipal()); System.out.println("输出登录者的凭证:"+userName); if(userName==null){ //未认证通过的情况 return null; } //模拟权限,手动添加 List<String> priList=new ArrayList<String>(); //依旧没有修改的权限 priList.add("user:add"); priList.add("user:delete"); priList.add("user:select"); priList.add("user:toList"); //创建 SimpleAuthorizationInfo 对象 SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo(); //添加角色, 通常不用角色判断,用权限判断 simpleAuthorizationInfo.addRole("role1"); for(String pri:priList){ //添加权限 simpleAuthorizationInfo.addStringPermission(pri); } return simpleAuthorizationInfo; }
这样,一个自定义的 Realm 就实现了
二.五 编写测试 MyRealmTest
package com.yjl.customer; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; public class MyRealmTest { public static void main(String[] args) throws Exception{ //1. 创建factory Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:customer.ini"); //2 获取实例 SecurityManager securityManager=factory.getInstance(); //3 设置 SecurityUtils.setSecurityManager(securityManager); //4. 获取 Subject Subject subject=SecurityUtils.getSubject(); //5. 设置token UsernamePasswordToken token=new UsernamePasswordToken("yuejl","1234"); //6. 执行登录操作 try{ subject.login(token); System.out.println(subject.getPrincipal()+"登录成功"); boolean flag=subject.hasRole("role1"); System.out.println("是否拥有角色 role1:"+flag); //关于角色验证的那些方法,都可以使用 flag=subject.hasRole("role2"); System.out.println("是否拥有角色role2:"+flag); //关于权限验证的那些方法,都可以使用 flag=subject.isPermitted("user:add"); System.out.println("是否拥有权限 user:add "+flag); flag=subject.isPermitted("user:update"); System.out.println("是否拥有权限 user:update "+flag); }catch(Exception e){ e.printStackTrace(); System.out.println("用户名或者密码不正确"); } } }
控制台打印输出:
权限检查了四次,所以调用了四次 doGetAuthorizationInfo() 方法, 很浪费资源, 所以常常会进行缓存处理,后面老蝴蝶会讲缓存处理。
发现,我们可以实现自定义的Realm.
然而,用户的密码和权限数据,都只是模拟数据,能不能换成真实的数据,真正从数据库里面查询的呢?