我不是语言的开发者,我只是它的搬运工。每进步一点,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配置文件相关推荐

  1. 安全认证框架Shiro (二)- shiro过滤器工作原理

    安全认证框架Shiro (二)- shiro过滤器工作原理 安全认证框架Shiro 二- shiro过滤器工作原理 第一前言 第二ShiroFilterFactoryBean入口 第三请求到来解析过程 ...

  2. shiro中ini配置文件

    1.shiro中ini配置文件理解 https://blog.csdn.net/achenyuan/article/details/70477818

  3. Shiro框架:Shiro简介、登陆认证入门程序、认证执行流程、使用自定义Realm进行登陆认证、Shiro的MD5散列算法

    一.Shiro介绍: 1.什么是shiro: (1)shiro是apache的一个开源框架,是一个权限管理的框架,实现用户认证.用户授权. (2)spring中有spring security,是一个 ...

  4. 鉴权/认证框架Spring Security和Apache Shiro比较

    参考: https://www.cnblogs.com/minxiang-luo/p/12492905.html https://www.javadevjournal.com/spring-boot/ ...

  5. mysql url认证_Springboot+shiro基于url身份认证和授权认证

    你还不会shiro吗?前奏 shiro核心配置文件(rolesFilter可选). 身份认证 多表登录源如何操作? 授权管理 如何解决界面多角色/资源问题 访问效果 权限管理在日常开发中很重要,所以硬 ...

  6. 安全框架Shiro的简单学习

    文章目录 Shiro 1.什么是 Shiro 2.功能介绍 3.Shiro 架构 3.1 Shiro 外部架构 3.2 Shiro 内部架构 4.Hello,Shiro 4.1 快速实践 5.Shir ...

  7. 这可能是史上功能最全的Java权限认证框架!

    点击关注公众号,Java干货及时送达 今天给大家推荐的这个开源项目超级棒,可能是史上功能最全的 Java 权限认证框架! 这个开源项目就是:sa-token . Sa-Token是什么? sa-tok ...

  8. Spring Security太复杂?试试这个轻量、强大、优雅的权限认证框架!

    各位程序猿小伙伴们,中秋快乐~在节日欢快的气氛中大家是不是还在奋笔疾书.沉浸在学习的海洋中呢? 小编这两天休息在家一直在想一个问题,那就是我们在开发SpringBoot项目的时候,该怎么做好权限认证呢 ...

  9. 再见Spring Security!推荐一款功能强大的权限认证框架,用起来够优雅!

    ‍ ‍在我们做SpringBoot项目的时候,认证授权是必不可少的功能!我们经常会选择Shiro.Spring Security这类权限认证框架来实现,但这些框架使用起来有点繁琐,而且功能也不够强大. ...

最新文章

  1. AI一分钟 | 程维成立滴滴股权投资公司;特斯拉董事会决定放弃私有化
  2. Permutations I II leetcode
  3. ASP.NET MVC教程:理解模型、视图和控制器(1)
  4. iphone怎么投屏到电脑_怎么把笔记本无线投屏到电视?好用的电脑投屏电视办法...
  5. 前端学Markdown
  6. java笔试之字符串反转
  7. linux 下使用genymotion
  8. 如何预防销售人员带走客户?
  9. atop用法_atop 使用详情
  10. 50Ω阻抗问题详解及射频电路设计中的阻抗匹配
  11. uib-datepicker-popup使用
  12. element 源码学习五 —— Notice 系列组件学习
  13. Unity动画 代码加载动画,可复用
  14. ❤️【独家】挑战全网最通俗易懂的神经网络的表达能力解释
  15. php saas,saas系统是什么
  16. 云计算时代,NGINX将是你的“必杀技”
  17. html页面实现打印
  18. sql server 函數
  19. [编程基础] Python命令行解析库argparse学习笔记
  20. 微软一个罕为人知的无敌命令

热门文章

  1. UI设计和平面设计学哪个好?平面设计转行做UI设计容易吗?
  2. 腾讯TDW:大型Hadoop集群应用[转载]
  3. promise 浏览器实现的源码_【更新中】浏览器相关的汇总
  4. SQLServer配置管理器
  5. Arduino小白的学习记录:RGB-LED传感器实验
  6. Smokeping在CentOs中安装方法
  7. .vue文件_Spring Boot + Vue 前后端分离,两种文件上传方式总结!
  8. Eq计算机中,EQ(均衡器)的用法
  9. android 抽屉菜单栏,android – 导航抽屉列表中的下划线菜单项
  10. 单元测试与集成测试、系统测试、测试过程管理、软件测试自动化的综合练习