shiro框架学习-5-自定义Realm
1. 自定义Realm基础
- 步骤:
- 创建一个类 ,继承AuthorizingRealm->AuthenticatingRealm->CachingRealm->Realm
- 重写授权方法 doGetAuthorizationInfo
- 重写认证方法 doGetAuthenticationInfo
- 方法:
- 当用户登陆的时候会调用 doGetAuthenticationInfo
- 进行权限校验的时候会调用: doGetAuthorizationInfo
- 对象介绍
- UsernamePasswordToken : 对应就是 shiro的token中有Principal和Credential
UsernamePasswordToken-》HostAuthenticationToken-》AuthenticationToken
- SimpleAuthorizationInfo:代表用户角色权限信息
- SimpleAuthenticationInfo :代表该用户的认证信息
- UsernamePasswordToken : 对应就是 shiro的token中有Principal和Credential
2. 自定义realm代码:
package net.xdclass.xdclassshiro; import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set; /**
* 自定义realm:继承AuthorizingRealm,重写抽象方法
*/
public class CustomRealm extends AuthorizingRealm { private final Map<String, String> userInfoMap = new HashMap<>();
{
userInfoMap.put("jack", "");
userInfoMap.put("xdclass", "");
} //role->permission 模拟数据库角色与权限的关系
private final Map<String, Set<String>> permissonMap = new HashMap<>();
{
Set<String> set1 = new HashSet<>();
Set<String> set2 = new HashSet<>();
set1.add("video:find");
set1.add("video:buy");
set2.add("video:add");
set2.add("video:delete");
permissonMap.put("jack", set1);
permissonMap.put("xdclass", set2);
} //user->role 模拟数据库角色与权限的关系
private final Map<String, Set<String>> roleMap = new HashMap<>();
{
Set<String> set1 = new HashSet<>();
Set<String> set2 = new HashSet<>();
set1.add("role1");
set1.add("role2");
set2.add("root");
roleMap.put("jack", set1);
roleMap.put("xdclass", set2);
} /**
* 校验权限时会调用这个方法,原理就是把角色和权限封装到SimpleAuthorizationInfo的实体中,shiro框架会帮我们进行权限的校验
* 最终org.apache.shiro.realm.AuthorizingRealm#hasRole(org.apache.shiro.subject.PrincipalCollection, java.lang.String)
* 方法会调用这个返回的simpleAuthorizationInfo
* @param principalCollection
* @return simpleAuthorizationInfo
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("权限:doGetAuthorizationInfo");
//获取用户主账户名
String name = (String) principalCollection.getPrimaryPrincipal();
//通过名称查找权限,简化操作(正常是通过名称找角色,通过角色查询对应的权限)
Set<String> permissions = getPermissonsByNameFromDB(name);
Set<String> roles = getRolesByNameFromDB(name);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
} private Set<String> getRolesByNameFromDB(String name) {
return roleMap.get(name);
} private Set<String> getPermissonsByNameFromDB(String name) {
return permissonMap.get(name);
} //用户登录的时候会调用该方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("认证: doGetAuthenticationInfo");
//从token中获取身份信息,token代表用户输入的信息
String name = (String) authenticationToken.getPrincipal();
//模拟从数据库读密码
String pwd = getPwdByUserNameFromDB(name); if (StringUtils.isBlank(pwd)) {
//这里判断是必须要加的,返回null,即为认证不通过!!!详见源码
return null; //匹配失败
}
/*useNmaePasswordToken中有用户的Principal和Credential
SimpleAuthorizationInfo代表用户的角色权限信息
SimpleAuthenticationInfo 代表该用户的认证信息*/
// this.getName()是获取CachingRealm的名字
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, pwd, this.getName());
return simpleAuthenticationInfo;
} private String getPwdByUserNameFromDB(String name) {
return userInfoMap.get(name);
}
}
代码测试:
package net.xdclass.xdclassshiro; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Before;
import org.junit.Test; /**
* 自定义Realm操作
*/
public class QuicksStratTest5_4 { private CustomRealm customRealm = new CustomRealm();
private DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
@Before
public void inint(){
//构建环境
defaultSecurityManager.setRealm(customRealm);
SecurityUtils.setSecurityManager(defaultSecurityManager);
} @Test
public void testAuthentication() {
//获取当前操作的主体
Subject subject = SecurityUtils.getSubject();
//模拟用户输入账号密码
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "123");
subject.login(usernamePasswordToken);
System.out.println("认证 结果:" + subject.isAuthenticated());
System.out.println("getPrincipal()=" + subject.getPrincipal()); System.out.println("调用权限校验:subject.hasRole方法会调用自定义realm重写的doGetAuthorizationInfo方法");
System.out.println("用户jack是否有root角色:" + subject.hasRole("root")); //false
// 权限校验
subject.checkRole("role1");
System.out.println("用户jack是否有role1角色:" + subject.hasRole("role1")); //true
System.out.println("用户jack是否有video:find权限:" + subject.isPermitted("video:find")); //true
} }
上面代码中 ,subject.login方法很关键,是所有认证授权逻辑的入口,跟踪一下代码,可以发现subject.login方法实际调用过程如下:
总结一下,shiro认证的主要流程就是
subject.login(usernamePasswordToken);
DelegatingSubject->login()
DefaultSecurityManager->login()
AuthenticatingSecurityManager->authenticate()
AbstractAuthenticator->authenticate()
ModularRealmAuthenticator->doAuthenticate()
ModularRealmAuthenticator->doSingleRealmAuthentication()
AuthenticatingRealm->getAuthenticationInfo()
需要注意的是:org.apache.shiro.authc.pam.ModularRealmAuthenticator#doAuthenticate 这个方法中,一般只配置一个realm,因此走的是doSingleRealmAuthentication 这个分支
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
this.assertRealmsConfigured();
Collection<Realm> realms = this.getRealms();
return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
}
doSingleRealmAuthentication 分支源码如下:
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
if (!realm.supports(token)) {
String msg = "Realm [" + realm + "] does not support authentication token [" + token + "]. Please ensure that the appropriate Realm implementation is " + "configured correctly or that the realm accepts AuthenticationTokens of this type.";
throw new UnsupportedTokenException(msg);
} else {
// 这里实际上调用的就是自定义realmz中重写了的getAuthenticationInfo方法,如果返回null,抛出UnknownAccountException,认证不通过
AuthenticationInfo info = realm.getAuthenticationInfo(token);
if (info == null) {
String msg = "Realm [" + realm + "] was unable to find account data for the " + "submitted AuthenticationToken [" + token + "].";
throw new UnknownAccountException(msg);
} else {
return info;
}
}
}
在源码中,获取 AuthenticationInfo 认证信息,如果为空的话,抛出异常,这也是为什么自定义realm中 doGetAuthenticationInfo 方法中判断密码不正确时要返回null的原因;
doSingleRealmAuthentication 方法中的 AuthenticationInfo info = realm.getAuthenticationInfo(token) 调用了org.apache.shiro.realm.AuthenticatingRealm 类的getAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)方法,这个方法里面,调用了一行: info = this.doGetAuthenticationInfo(token),查找其实现类,就是我们自定义realm重写的的doGetAuthenticationInfo方法。
同理,shiro授权的主要流程如下:subject.checkRole方法是授权流程的入口
subject.checkRole("admin")
DelegatingSubject->checkRole()
AuthorizingSecurityManager->checkRole()
ModularRealmAuthorizer->checkRole()
AuthorizingRealm->hasRole()
AuthorizingRealm->doGetAuthorizationInfo()
shiro框架学习-5-自定义Realm的更多相关文章
- Shiro入门学习之自定义Realm实现授权(五)
一.自定义Realm授权 前提:认证通过,查看Realm接口的继承关系结构图如下,要想通过自定义的Realm实现授权,只需继承AuthorizingRealm并重写方法即可 二.实现过程 1.新建mo ...
- Shiro入门学习之自定义Realm实现认证(四)
一.概述 Shirom默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,而大部分情况下需要从系统数据库中读取用户信息,所以需要实现自定义Realm,Realm接口如下: ...
- Shiro入门学习---使用自定义Realm完成认证|练气中期
写在前面 在上一篇文章<shiro认证流程源码分析--练气初期>当中,我们简单分析了一下shiro的认证流程.不难发现,如果我们需要使用其他数据源的信息完成认证操作,我们需要自定义Real ...
- shiro框架学习-3- Shiro内置realm
1. shiro默认自带的realm和常见使用方法 realm作用:Shiro 从 Realm 获取安全数据 默认自带的realm:idae查看realm继承关系,有默认实现和自定义继承的realm ...
- shiro框架学习-8-shiro缓存
1. shiro进行认证授权时会查询数据库获取用户角色权限信息,每次登录都会去查询,这样对性能会又影响.可以设置缓存,查询时先去缓存中查找,缓存中没有再去数据库查询. 从shiro的架构图中可以看到有 ...
- shiro框架学习-1-shiro基本概念
1. 什么是权限控制 基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源, ...
- shiro框架学习-6-Shiro内置的Filter过滤器及数据加解密
1. shiro的核心过滤器定义在枚举类DefaultFilter 中,一共有11个 ,配置哪个路径对应哪个拦截器进行处理 // // Source code recreated from a .c ...
- shiro框架学习-9-shiroSession
1.什么是会话session : 用户和程序直接的链接,程序可以根据session识别到哪个用户,和javaweb中的session类似 2. 什么是会话管理器SessionManager : 会话管 ...
- shiro框架学习-4- Shiro内置JdbcRealm
1. JdbcRealm 数据库准备 JdbcRealm就是用户的角色,权限都从数据库中读取,也就是用来进行用户认证授权的安全数据源更换为从数据库中读取,其他没有差别,首先在数据库创建三张表: CR ...
随机推荐
- Day05:循环问题 / 数组
循环嵌套 循环结构中包含完整的循环结构. 注意: 循环嵌套不限层次 各种循环语句都可以互相嵌套 内层循环中出现的break和continue只作用在内层循环中 外层循环循环一次 内层循环循环一遍 Ja ...
- jmeter业务建模中遇到的问题
1.jmeter函数助手中的jexl3函数,不支持${__jexl3(15<${__Random(1,100,)}<36,)}这种写法,须这样写${__jexl3(15<${__Ra ...
- 学习Go语言(一)环境安装及HelloWorld
自己开发的时候,一般用Java和C#居多,偶尔也用Python做点东东. 想体验一下比较“现代”语言,思来想去就来体验一下Go语言. 闲话少叙,言归正传,首先就是环境安装,这个轻车熟路: (1)到官网 ...
- 【VS开发】【智能语音处理】解读男女声音的区别:亮度,糙度
1. 男女声音的本质不同:音高不同 这是废话,地球人都知道.都说女声比男声高八度,其实不能,高4-6度差不多. 2. 男女声音的不同:亮度 从直观上这个很好理解,女声普遍更"亮", ...
- python 并发编程 多线程 信号量
一 信号量 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行 如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群 ...
- BadUsb简单用法示例
所需硬件 badusb 自行淘宝 驱动程序会自动安装,若未自动安装自行百度 所需工具 arduino-1.5.5-r2用于编辑代码并上传至badusb badusb连接至电脑 打开ard ...
- 第八周课程报告&&实验报告六
Java实验报告 班级 计科一班 学号 20188390 姓名 宋志豪 实验四 类的继承 实验目的 理解异常的基本概念: 掌握异常处理方法及熟悉常见异常的捕获方法. 实验要求 练习捕获异常.声明异常. ...
- CSS基本样式-文本属性
字体属性 文本属性呢,自我认为就是写文档的一些格式属性,例如:字体颜色,字体加粗,字体大小,字体类型等,而且我们在输出测试用例报告的时候也可以用到这些属性,对测试报告进行优化. <html> ...
- HDU 1069 Monkey and Banana (动态规划、上升子序列最大和)
Monkey and Banana Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- 【转帖】国产x86处理器KX-6000发布
国产最先进x86处理器KX-6000发布:8核3.0GHz 力压酷睿i5处理器 https://www.cnbeta.com/articles/tech/858981.htm 全网所有的网页都写错了 ...