安全认证框架Shiro (一)- ini配置文件
我不是语言的开发者,我只是它的搬运工。每进步一点,5年之后你也是个人物
为什么看网上的例子都喜欢用ini格式文件,为什么不用.propertes或xml。
我们来看看一个ini格式文件text.ini:
[main]
activeDirectoryRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
activeDirectoryRealm.systemUsername = uid=admin,ou=system
activeDirectoryRealm.systemPassword = secret
activeDirectoryRealm.searchBase = o=sevenSeas,ou=people
activeDirectoryRealm.url = ldap://localhost:10389
[users]
name=cy
pwd=123
看了源码才知道,原来shiro框架里新造了一个Ini类,当我们传入资源时,Ini里使用流一行一行的读资源,当遇到”#”或”;”开头的则直接跳过;
遇到“[*]”则将中括号里的字符串看过Section(区块)的key,后面一行一行都视做该区域的内容直到遇到新的中括号。随后再解读区域下面多行字符串(至少一行),如果遇到“:”或“=”或“”,则前面当做key,后面的则是为value(同时会过滤掉value里前后空格以及“=”前后空格),存到一个Section里,最后把所有行解析完后放到名为sections的HashMap里。
IniSecurityManagerFacotry继承自IniFactorySupport,而IniFactorySupport有个setIni()方法将解析出来的Ini结构数据保存到该类里,其它什么都不做。
1.[users]部分
#提供了对用户/密码及其角色的配置,用户名=密码,角色1,角色2
username=password,role1,role2
例如:
配置用户名/密码及其角色,格式:“用户名=密码,角色1,角色2”,角色部分可省略。如:
[users]
zhang=123,role1,role2
wang=123
2. [roles]
#提供了角色及权限之间关系的配置,角色=权限1,权限2
role1=permission1,permission2
例如:
配置角色及权限之间的关系,格式:“角色=权限1,权限2”;如:
[roles]
role1=user:create,user:update
role2=*
如果只有角色没有对应的权限,可以不配roles
3. [main]部分
提供了对根对象securityManager及其依赖对象的配置。
创建对象
securityManager=org.apache.shiro.mgt.DefaultSecurityManager
其构造器必须是public空参构造器,通过反射创建相应的实例。
1、对象名=全限定类名 相对于调用public无参构造器创建对象
2、对象名.属性名=值 相当于调用setter方法设置常量值
3、对象名.属性名=$对象引用 相当于调用setter方法设置对象引用
4.[urls]
#用于web,提供了对web url拦截相关的配置,url=拦截器[参数],拦截器
/index.html = anon
/admin/** = authc, roles[admin],perms["permission1"]
5.非标签。不同种类数据注入方式
5.1 Map setter注入
即格式是:map=key:value,key:value,可以注入常量及引用值,常量的话都看作字符串
例如:
authenticator.map=$jdbcRealm:$jdbcRealm,1:1,key:abc
5.2Array/Set/List setter注入
多个之间通过“,”分割。
例如:
authenticator.array=1,2,3
authenticator.set=$jdbcRealm,$jdbcRealm
5.3嵌套属性setter注入
例如:
securityManager.authenticator.authenticationStrategy=$authenticationStrategy
5.4对象引用setter注入
authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator
securityManager.authenticator=$authenticator
5.5创建对象
其构造器必须是public空参构造器,通过反射创建相应的实例
securityManager=org.apache.shiro.mgt.DefaultSecurityManager
源码解析:
当配置文件里出现[users]或[roles]时,IniSecurityManagerFacotry会初始化一个IniRealm做为数据源,把ini传入到IniRealm里,IniRealm的name是“iniRealm”。
protected Realm createRealm(Ini ini) {//IniRealm realm = new IniRealm(ini); changed to support SHIRO-322IniRealm realm = new IniRealm();realm.setName(INI_REALM_NAME);realm.setIni(ini); //added for SHIRO-322return realm;}
并把realm存到securityManager的realms属性集合里。
当出现[main]时,说明是主配置。看下面的解析图:
public Map<String, ?> buildObjects(Map<String, String> kvPairs) {if (kvPairs != null && !kvPairs.isEmpty()) {// Separate key value pairs into object declarations and property assignment// so that all objects can be created up front//https://issues.apache.org/jira/browse/SHIRO-85 - need to use LinkedHashMaps here:Map<String, String> instanceMap = new LinkedHashMap<String, String>();Map<String, String> propertyMap = new LinkedHashMap<String, String>();for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
//不出现“.”或者以“.class”结尾if (entry.getKey().indexOf('.') < 0 || entry.getKey().endsWith(".class")) {instanceMap.put(entry.getKey(), entry.getValue());} else {propertyMap.put(entry.getKey(), entry.getValue());}}// Create all instancesfor (Map.Entry<String, String> entry : instanceMap.entrySet()) {createNewInstance((Map<String, Object>) objects, entry.getKey(), entry.getValue());}// Set all propertiesfor (Map.Entry<String, String> entry : propertyMap.entrySet()) {applyProperty(entry.getKey(), entry.getValue(), objects);}}//SHIRO-413: init method must be called for constructed objects that are InitializableLifecycleUtils.init(objects.values());return objects;}
这里当key里不出现“.”或者以“.class”结尾,说明是需要实例化的类,value值即为类的全限名,这些实例最张会被反射注入到DefaultSecurityManager的实例securityManager里。否则视为属性,用反射去设置上次实例化的对象属性值。其中objects是包含着key是“securityManager”,value为DefaultSecurityManager对象的Map对象。所有被main标记的都会被注入到securityManager”。
当不出现“[]”时,“”空即为sections的key。只要第一行没出现”[]”则一定会出现key为空的map的键值对。(注意一点,如果没有[main],则取sections里””即空为的key的数据做为主配置)
当我们调用IniSecurityManagerFacotry里getInstance()方法时,会根据是否有ini数据来调用不同的方法创建不同的SecurityManager.当有ini时调用
protected SecurityManager createInstance(Ini ini) {if (CollectionUtils.isEmpty(ini)) {throw new NullPointerException("Ini argument cannot be null or empty.");}
//createSecurityManager()才是重点SecurityManager securityManager = createSecurityManager(ini);if (securityManager == null) {String msg = SecurityManager.class + " instance cannot be null.";throw new ConfigurationException(msg);}return securityManager;}
当没有时调用:
protected SecurityManager createDefaultInstance() {return new DefaultSecurityManager();
}
一般将realm标记为[main],那么会生成Realm的实例,保存到DefaultSecurityManager的realms集合里,这样securityManager就有数据源了。
再看看网上常用的用shiro的API的例子:
Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("auth.ini");// Setting up the SecurityManager...org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();SecurityUtils.setSecurityManager(securityManager);Subject user = SecurityUtils.getSubject();
前面两句很容易理解,就是把配置文件里配置的参数放到Ini里,并把ini传给Realm实例,同时造一个DefaultSecurityManager实例。那后面两句如何理解呢,从字面上其实很容易看出来,将securityManager放到SecurityUtils里,同时从SecurityUtils里取Subject。Subject相当于当前线程里的相当“用户”,体现在程序里即是保存用户相关身份和凭证等的信息,以及操作方法。
看看SecurityUtils的setSecurityManager()代码:
public static void setSecurityManager(SecurityManager securityManager) {SecurityUtils.securityManager = securityManager;}
和getSubject():
public static Subject getSubject() {
//从线程的上下文环境里取Subject,如果有就返回,没有则创建一个Subect返回并绑定到ThreadContext上。Subject subject = ThreadContext.getSubject();if (subject == null) {subject = (new Subject.Builder()).buildSubject();ThreadContext.bind(subject);}return subject;}
再沿深一个SecurityUtils,其实里面只操作了Subject和SecurityManager.
如果我们跳出Ini配置的束缚,我们应该能得到结论,我们应该给SecurityManager提供一个或多个Realm对象在到realms里,比如在spring框架的xml里配,SecurityUtils作用把Subject和SecurityManager关联起来了,只要能把当前用户信息(Subject)和SecurityManager(包含验证的数据来源信息等配置)搭上话,那么要么我们自己用API做事,要么Spring帮我们管理都很方便。
6.断点分析
我们以如下的配置文件为便说明怎么解析的:
[main]
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
jdbcRealm.dataSource=$dataSource[users]
zhang=123,role1,role2
wang=123[roles]
#对资源user拥有create、update权限
role1=user:create,user:update
#对资源user拥有create、delete权限
role2=user:create,user:delete[urls]
/index.html = anon
/admin/** = authc, roles[admin], perms["permission1"]
1.
当执行到Ini里的load()方法里时。读取到main块区域出的多行,看到sectionContext内容是”[main]”标签下的多行内容,直到遇到新的标签为止。
2.
在addSection()将上面的sectionContext的内容解析放到Section里,看红色标注的内容,其实Section里用LinkedHaspMap保存Key-value。同时也会将”[users]”,”[roles]”,”[urls]”解析到Section里,并用key-value保存用“=”设置的读,section就是标签名如“main”,最后所以的section会保存到Ini里的sections里,该类是LinkedHaspMap类型。
users标签处理:
SimpleAccount结构体保存users标签里数据并最终放到SimpleAccountRealm的users字典里。
到此解析ini文件已经结束。
再看看配置的数据怎么用的。iniSecurityManagerFactory.getInstance() 方法里最重要的是createSecurityManager()方法,看看这个方法的信息:
第一行createDefaults();返回的Map里包含了DefaultSecurityManager和IniRealm,其中IniRealm是包含ini配置文件的Realm。
第二行buildInstances()是将ini配置文件里的类实例化属性反射set。
结里如下图:
是不是和配置文件里一样:
[main]
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
jdbcRealm.dataSource=$dataSource
下面确定是否SecurityManager实例里realms是否有数据,如果没有则遍历上图里所以实例,如果是Realm接口的子类则将该实例放入realms里,同时SecurityManager实例里的authenticator验证器如果是ModularRealmAuthenticator(默认是ModularRealmAuthenticator验证器)则也将realms数据放入authenticator里。可见authenticator已经拿到了数据源,如果我们做验证的话,authenticator会从realm里拿数据做比较。
至些iniSecurityManagerFactory.getInstance()方法执行完成,主要逻辑就是把main标记的类实例话,将属性设置到类上,且最终实例话的类设置到securityManager的属性里并返回instancee。其它什么也不做,roles,users,urls不会在此处理。
SecurityUtils.setSecurityManager(securityManager)方法没什么逻辑,只是set方法,不值得跟踪。
Subjectsubject = SecurityUtils.getSubject():从当前线程上下文里取,如果没有则创建一个并缓存起来。
重点看subject. login(token),token会传到authenticator里,看看数据:
在验证器里的doAuthenticate方法里取上次赋值的2个realm实例。根据数量选择不同方法,我们有2个,所以执行doMultiRealmAuthentication()方法。在doMultiRealmAuthentication()会遍历realms里所以realm对象,并将相关数据传入realm里。
先看IniRealm:
先看有没有缓存,如果有则说明已经验证过了,直接跳过,如果没有则去执行doGetAuthenticationInfo()方法。
如果查询到信息则返回tokenInfo,取得的TokenInfo与token在assertCredentialsMatch方法里验证是否匹配。
匹配器里方法做最终凭证匹配,如果相同则返回true,否则为false.
验证成功后会一层层返回,
1.会通知该验证器所以的监听器
2. 创建SubjectContext,缓存一些数据,并保存到Subject。下次验证时先从缓存数据里取,如果验证成功则无需再做验证。
安全认证框架Shiro (一)- ini配置文件相关推荐
- 安全认证框架Shiro (二)- shiro过滤器工作原理
安全认证框架Shiro (二)- shiro过滤器工作原理 安全认证框架Shiro 二- shiro过滤器工作原理 第一前言 第二ShiroFilterFactoryBean入口 第三请求到来解析过程 ...
- shiro中ini配置文件
1.shiro中ini配置文件理解 https://blog.csdn.net/achenyuan/article/details/70477818
- Shiro框架:Shiro简介、登陆认证入门程序、认证执行流程、使用自定义Realm进行登陆认证、Shiro的MD5散列算法
一.Shiro介绍: 1.什么是shiro: (1)shiro是apache的一个开源框架,是一个权限管理的框架,实现用户认证.用户授权. (2)spring中有spring security,是一个 ...
- 鉴权/认证框架Spring Security和Apache Shiro比较
参考: https://www.cnblogs.com/minxiang-luo/p/12492905.html https://www.javadevjournal.com/spring-boot/ ...
- mysql url认证_Springboot+shiro基于url身份认证和授权认证
你还不会shiro吗?前奏 shiro核心配置文件(rolesFilter可选). 身份认证 多表登录源如何操作? 授权管理 如何解决界面多角色/资源问题 访问效果 权限管理在日常开发中很重要,所以硬 ...
- 安全框架Shiro的简单学习
文章目录 Shiro 1.什么是 Shiro 2.功能介绍 3.Shiro 架构 3.1 Shiro 外部架构 3.2 Shiro 内部架构 4.Hello,Shiro 4.1 快速实践 5.Shir ...
- 这可能是史上功能最全的Java权限认证框架!
点击关注公众号,Java干货及时送达 今天给大家推荐的这个开源项目超级棒,可能是史上功能最全的 Java 权限认证框架! 这个开源项目就是:sa-token . Sa-Token是什么? sa-tok ...
- Spring Security太复杂?试试这个轻量、强大、优雅的权限认证框架!
各位程序猿小伙伴们,中秋快乐~在节日欢快的气氛中大家是不是还在奋笔疾书.沉浸在学习的海洋中呢? 小编这两天休息在家一直在想一个问题,那就是我们在开发SpringBoot项目的时候,该怎么做好权限认证呢 ...
- 再见Spring Security!推荐一款功能强大的权限认证框架,用起来够优雅!
在我们做SpringBoot项目的时候,认证授权是必不可少的功能!我们经常会选择Shiro.Spring Security这类权限认证框架来实现,但这些框架使用起来有点繁琐,而且功能也不够强大. ...
最新文章
- AI一分钟 | 程维成立滴滴股权投资公司;特斯拉董事会决定放弃私有化
- Permutations I II leetcode
- ASP.NET MVC教程:理解模型、视图和控制器(1)
- iphone怎么投屏到电脑_怎么把笔记本无线投屏到电视?好用的电脑投屏电视办法...
- 前端学Markdown
- java笔试之字符串反转
- linux 下使用genymotion
- 如何预防销售人员带走客户?
- atop用法_atop 使用详情
- 50Ω阻抗问题详解及射频电路设计中的阻抗匹配
- uib-datepicker-popup使用
- element 源码学习五 —— Notice 系列组件学习
- Unity动画 代码加载动画,可复用
- ❤️【独家】挑战全网最通俗易懂的神经网络的表达能力解释
- php saas,saas系统是什么
- 云计算时代,NGINX将是你的“必杀技”
- html页面实现打印
- sql server 函數
- [编程基础] Python命令行解析库argparse学习笔记
- 微软一个罕为人知的无敌命令
热门文章
- UI设计和平面设计学哪个好?平面设计转行做UI设计容易吗?
- 腾讯TDW:大型Hadoop集群应用[转载]
- promise 浏览器实现的源码_【更新中】浏览器相关的汇总
- SQLServer配置管理器
- Arduino小白的学习记录:RGB-LED传感器实验
- Smokeping在CentOs中安装方法
- .vue文件_Spring Boot + Vue 前后端分离,两种文件上传方式总结!
- Eq计算机中,EQ(均衡器)的用法
- android 抽屉菜单栏,android – 导航抽屉列表中的下划线菜单项
- 单元测试与集成测试、系统测试、测试过程管理、软件测试自动化的综合练习