本来这篇文章是想写从Factory加载ini配置到生成securityManager的过程的。。。。但是貌似涉及的东西有点多。。。我学的又比较慢。。。很多类都来不及研究,我又怕等我后面的研究了前面的都忘记了。。。所以就先与大家分享一下我对Shiro里Ini这个类的研究。

简介

我感觉其实利用ini来配置shiro在实际应用中应该不是很多,我想大部分java应用还是web类型的,数据也应该储存在数据库里。。不会简简单单的存在.ini这样的配置文件中的。。。但是对于学习Shiro来说,我觉得研究一下这个类也无妨。。Ini配置文件是怎么写的,我就不介绍了,网上一大把。。我就说说Ini这个类的主要作用与一些值得注意的地方。

Shiro加载.ini配置文件以后会生成一个Ini类的对象,这个Ini类的对象就储存了配置文件的信息。

Ini

Ini实现了Map<String, Ini.Section>,它又有一个sections对象,也是Map<String, Section>类型的,所有Ini类上的Map接口的操作实际上都是操作sections对象。

Ini的主要作用就是解析.ini文件,从.ini中解析出多个section,放到sections成员域中。

 public class Ini implements Map<String, Ini.Section> {

     private static transient final Logger log = LoggerFactory.getLogger(Ini.class);

     public static final String DEFAULT_SECTION_NAME = ""; //empty string means the first unnamed section
public static final String DEFAULT_CHARSET_NAME = "UTF-8"; public static final String COMMENT_POUND = "#";
public static final String COMMENT_SEMICOLON = ";";
public static final String SECTION_PREFIX = "[";
public static final String SECTION_SUFFIX = "]"; protected static final char ESCAPE_TOKEN = '\\'; private final Map<String, Section> sections; ...............
}

Map中key是String类型的(废话,大家都知道。。。),value是Section类型的,Section是Ini类里的一个静态内部类。

那么这么一个Map有什么用处呢?用Shiro的话来回答:

An Ini instance is a map of Sections, keyed by section name. Each Section is itself a map of String name/value pairs. Name/value pairs are guaranteed to be unique within each Section only - not across the entire Ini instance.

意思就是说Ini这个Map,key是Section的名字,就是ini配置文件中[]中的部分,value则是Section。

.ini文件中的key并不是整个.ini唯一的,而是每个section唯一的,要问为什么的话,就是因为每个section都是Ini类中的Map中的一个键值对,而.ini文件中的key又是section中的Map中的键值对,所以并不会相互影响(绕口令....看了Section那一节应该就明白了...),这点请注意。

介绍一下Ini中的一些方法:

     public static Ini fromResourcePath(String resourcePath) throws ConfigurationException {
if (!StringUtils.hasLength(resourcePath)) {
throw new IllegalArgumentException("Resource Path argument cannot be null or empty.");
}
Ini ini = new Ini();
ini.loadFromPath(resourcePath);
return ini;
}

这个方法很短也很简单,介绍他主要是因为它会被Factory调用,来加载ini文件,Factory中会new一个Ini,然后调用这个方法。

另外这个方法会调用loadFromPath来加载ini配置。

而loadFromPath会调用load方法来加载ini配置,所以说其实这些方法都没有做什么事实,最多就是读取文件,最后其实都是调用了load方法来真正解析ini文件。

load

load方法被重载了10000次。。。。。最终都会调用load(Scanner scanner)这个方法。。。

   public void load(Scanner scanner) {

         String sectionName = DEFAULT_SECTION_NAME;
StringBuilder sectionContent = new StringBuilder(); while (scanner.hasNextLine()) { String rawLine = scanner.nextLine();
String line = StringUtils.clean(rawLine); if (line == null || line.startsWith(COMMENT_POUND) || line.startsWith(COMMENT_SEMICOLON)) {
//skip empty lines and comments:
continue;
} String newSectionName = getSectionName(line);
if (newSectionName != null) {
//found a new section - convert the currently buffered one into a Section object
addSection(sectionName, sectionContent); //reset the buffer for the new section:
sectionContent = new StringBuilder(); sectionName = newSectionName; if (log.isDebugEnabled()) {
log.debug("Parsing " + SECTION_PREFIX + sectionName + SECTION_SUFFIX);
}
} else {
//normal line - add it to the existing content buffer:
sectionContent.append(rawLine).append("\n");
}
} //finish any remaining buffered content:
addSection(sectionName, sectionContent);
}

这个方法就是一行一行读取ini,如果是;或者#开头,则认为这是注释行,忽略。

否则就去调用getSectionName(第16行)这个方法。这个方法会判断一行是否是[开头,]结尾,是说明这个是一个section的name,否则就是section的content。

如果是下一个section的name,就先保存上个section的name和content(第19行),并重新赋值sectionName局部变量。

不是下一个section的name,就说明这一行是content,那么就调用31行的append方法,把content append到sectionContent上面。

这个方法做完,ini配置文件就被解析成了很多小的Section,也就是说完成了sections属性的解析,后面每个section的解析就留到Section这个静态内部类中来完成。

Section

Section又是一个Map(Map<String, String>,默认实现是LinkedHashMap)。

 public static class Section implements Map<String, String> {
private final String name;
private final Map<String, String> props;
...............
}

section的key是section的名字,value是这个section中的所有文字信息(每一行文字信息做String.trim()以后再拼接起来)。

看上面的代码,Scetion中有一个props属性,也是Map类型,props代表了.ini配置文件中section中每一行的信息。

比如在ini文件中有这么一行:

authenticator=org.apache.shiro.authc.pam.ModularRealmAuthenticator

那key就是authenticator,value就是org.apache.shiro.authc.pam.ModularRealmAuthenticator。

section中有几个方法比较重要,我想介绍一下。

toMapProps

这个方法的用处就是把section中整整一大节的文字信息转化成很多小行,每一行都包含了键值对,存进props中。

         private static Map<String, String> toMapProps(String content) {
Map<String, String> props = new LinkedHashMap<String, String>();
String line;
StringBuilder lineBuffer = new StringBuilder();
Scanner scanner = new Scanner(content);
while (scanner.hasNextLine()) {
line = StringUtils.clean(scanner.nextLine());
if (isContinued(line)) {
//strip off the last continuation backslash:
line = line.substring(0, line.length() - 1);
lineBuffer.append(line);
continue;
} else {
lineBuffer.append(line);
}
line = lineBuffer.toString();
lineBuffer = new StringBuilder();
String[] kvPair = splitKeyValue(line);
props.put(kvPair[0], kvPair[1]);
} return props;
}

整个方法其实还是比较简单的,就是一行一行读取section中的文字,然后调用splitKeyValue方法,把每一行的键值对分别取出来,然后再放到props这个map里。

这也就是section要做的主要内容,把一整节文字拆分成很多小的键值对,完成解析ini。

这个方法中会调用其他2个方法,isContinued(第8行)和splitKeyValue(第18行),这2个方法都值得一说。

isContinued

  protected static boolean isContinued(String line) {
if (!StringUtils.hasText(line)) {
return false;
}
int length = line.length();
//find the number of backslashes at the end of the line. If an even number, the
//backslashes are considered escaped. If an odd number, the line is considered continued on the next line
int backslashCount = 0;
for (int i = length - 1; i > 0; i--) {
if (line.charAt(i) == ESCAPE_TOKEN) {
backslashCount++;
} else {
break;
}
}
return backslashCount % 2 != 0;
}

上面说道toMapProps会一行一行解析section中的文字,其实不是,可能是2行,甚至多行,这要看每行最后有没有\(backslashes,反斜杠)这个符号。如果每行最后是一个\,那代表这一行其实文字太长了,写不下才换行,下一行和上面一行是连在一起的。所以说:

 security\
Manager.realms=$myRealm1,$myRealm3

 securityManager.realms=$myRealm1,$myRealm3

是一样的东西,这点大家值得注意,文字太长的时候可以用\换行。

但是\也有转义的意思,如果2个\\一起在最后,并不代表2行文字是同一行。所以要用isContinued方法来判断最后有几个\,奇数就说明2行文字是连续的,偶数说明\只是转义用的。

splitKeyValue

  protected static String[] splitKeyValue(String keyValueLine) {
String line = StringUtils.clean(keyValueLine);
if (line == null) {
return null;
}
StringBuilder keyBuffer = new StringBuilder();
StringBuilder valueBuffer = new StringBuilder(); boolean buildingKey = true; //we'll build the value next: for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i); if (buildingKey) {
if (isKeyValueSeparatorChar(c) && !isCharEscaped(line, i)) {
buildingKey = false;//now start building the value
} else {
keyBuffer.append(c);
}
} else {
if (valueBuffer.length() == 0 && isKeyValueSeparatorChar(c) && !isCharEscaped(line, i)) {
//swallow the separator chars before we start building the value
} else {
valueBuffer.append(c);
}
}
} String key = StringUtils.clean(keyBuffer.toString());
String value = StringUtils.clean(valueBuffer.toString()); if (key == null || value == null) {
String msg = "Line argument must contain a key and a value. Only one string token was found.";
throw new IllegalArgumentException(msg);
} log.trace("Discovered key/value pair: {}={}", key, value); return new String[]{key, value};
}

这个方法其实并没有什么特别的,就是一个char一个读取过去,读到:或者=的时候说明前面都是key的部分,后面都是value的部分。但是因为有\的存在,所以读取可能会稍微麻烦一点,但是并不是很难理解,大家可以自己看看源码。其实只要知道这个方法干嘛用的就行了,并不用深究。

以上就是我对Ini类的理解。

Apache Shiro 学习记录5的更多相关文章

  1. Apache Shiro 学习记录1

    最近几天在学习Apache Shiro......看了一些大神们的教程.....感觉收获不少.....但是毕竟教程也只是指引一下方向....即使是精品教程,仍然有很多东西都没有说明....所以自己也稍 ...

  2. Apache Shiro 学习记录4

    今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...

  3. Apache Shiro 学习记录2

    写完上篇随笔以后(链接).....我也想自己尝试一下写一个Strategy.....Shiro自带了3个Strategy,教程(链接)里作者也给了2个.....我想写个都不一样的策略.....看来看去 ...

  4. Apache Shiro 学习记录3

    晚上看了教程的第三章....感觉Shiro字符串权限很好用....但是教程举的例子太少了.....而且有些地方讲的不是很清楚....所以我也自己测试了一下....记录一下测试的结果.... (1) * ...

  5. Apache Shiro学习-2-Apache Shiro Web Support

     Apache Shiro Web Support  1. 配置 将 Shiro 整合到 Web 应用中的最简单方式是在 web.xml 的 Servlet ContextListener 和 Fil ...

  6. apache shiro学习笔记

    一.权限概述 1.1 认证与授权 认证:系统提供的用于识别用户身份的功能,通常登录功能就是认证功能-----让系统知道你是谁?? 授权:系统授予用户可以访问哪些功能的许可(证书)----让系统知道你能 ...

  7. Apache shiro学习总结

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  8. Java安全框架 Apache Shiro学习-1-ini 配置

    简单登录流程: 1.  SecurityManager   2.  SecurityUtils.setSecurityManager 3.  SecurityUtils.getSubject     ...

  9. shiro学习记录(三)

    1.使用ehcache缓存权限数据 ehcache是专门缓存插件,可以缓存Java对象,提高系统性能. l ehcache提供的jar包: 第一步:在pom.xml文件中引入ehcache的依赖 &l ...

随机推荐

  1. Drop all the tables, stored procedures, triggers, constraints and all the dependencies in one SQL statement

    Is there any way in which I can clean a database in SQl Server 2005 by dropping all the tables and d ...

  2. JAVA NIO Buffer

    所谓的输入,输出,就是把数据移除或移入缓冲区.   硬件不能直接访问用户控件(JVM). 基于存储的硬件设备操控的是固定大小的数据块儿,用户请求的是任意大小的或非对齐的数据块儿.   虚拟内存:使用虚 ...

  3. 父容器利用opacity设置透明后,子元素跟着变透明的解决方案

    背景半透明,子元素不透明的效果经常需要用到.通常对父容器使用opacity属性时,子元素也跟着变透明,所以不妨设置父容器的 background-color:rgba(r,g,b,x); 其中x取值从 ...

  4. Crimm Imageshop 2.3。

    下载地址:http://files.cnblogs.com/Imageshop/ImageShop.rar 一款体积小,能绿色执行,又功能丰富的图像处理软件. Imageshop2.3为单EXE文件, ...

  5. SCNU 2015ACM新生赛初赛【1006. 3D打印】解题报告

            题目链接详见SCNU 2015新生网络赛 1006. 3D打印 .出题思路来自codevs 3288. 积木大赛,属于模拟题.         首先我们把“选择从第L部分到第R部分”理 ...

  6. Hibernate 缓存机制浅析

    1. 为什么要用 Hibernate 缓存? Hibernate是一个持久层框架,经常访问物理数据库. 为了降低应用程序对物理数据源访问的频次,从而提高应用程序的运行性能. 缓存内的数据是对物理数据源 ...

  7. ACM/ICPC2016 青岛区域赛

    A(hdu5982).(模拟) 题意:输入n对数,将每对数相乘并相加 分析:模拟 B(hdu5983).(模拟) 题意:给你一个二阶魔方,问能否通过一次旋转使得给定魔方的每个面颜色相同 分析:模拟 C ...

  8. C#实现Levenshtein distance最小编辑距离算法

    Levenshtein distance,中文名为最小编辑距离,其目的是找出两个字符串之间需要改动多少个字符后变成一致.该算法使用了动态规划的算法策略,该问题具备最优子结构,最小编辑距离包含子最小编辑 ...

  9. go 常用包

    标准的 Go 代码库中包含了大量的包,并且在安装 Go 的时候多数会伴随一起安 装.浏览 $GOROOT/src/pkg 目录并且查看那些包会非常有启发. fmt:包 fmt 实现了格式化的 I/O ...

  10. python网络编程-OSI七层模型详解

    OSI 七层模型通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯,因此其最主要的功能就是帮助不同类型的主机实现数据传输 . 完成中继功能的节点通常称为中继系统.在OSI七层模型中,处于 ...