zoukankan      html  css  js  c++  java
  • Apache Shiro 学习记录5

    本来这篇文章是想写从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成员域中。

     1 public class Ini implements Map<String, Ini.Section> {
     2 
     3     private static transient final Logger log = LoggerFactory.getLogger(Ini.class);
     4 
     5     public static final String DEFAULT_SECTION_NAME = ""; //empty string means the first unnamed section
     6     public static final String DEFAULT_CHARSET_NAME = "UTF-8";
     7 
     8     public static final String COMMENT_POUND = "#";
     9     public static final String COMMENT_SEMICOLON = ";";
    10     public static final String SECTION_PREFIX = "[";
    11     public static final String SECTION_SUFFIX = "]";
    12 
    13     protected static final char ESCAPE_TOKEN = '\';
    14 
    15     private final Map<String, Section> sections;
    16 
    17     ...............
    18 }

    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中的一些方法:

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

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

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

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

    load

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

     1   public void load(Scanner scanner) {
     2 
     3         String sectionName = DEFAULT_SECTION_NAME;
     4         StringBuilder sectionContent = new StringBuilder();
     5 
     6         while (scanner.hasNextLine()) {
     7 
     8             String rawLine = scanner.nextLine();
     9             String line = StringUtils.clean(rawLine);
    10 
    11             if (line == null || line.startsWith(COMMENT_POUND) || line.startsWith(COMMENT_SEMICOLON)) {
    12                 //skip empty lines and comments:
    13                 continue;
    14             }
    15 
    16             String newSectionName = getSectionName(line);
    17             if (newSectionName != null) {
    18                 //found a new section - convert the currently buffered one into a Section object
    19                 addSection(sectionName, sectionContent);
    20 
    21                 //reset the buffer for the new section:
    22                 sectionContent = new StringBuilder();
    23 
    24                 sectionName = newSectionName;
    25 
    26                 if (log.isDebugEnabled()) {
    27                     log.debug("Parsing " + SECTION_PREFIX + sectionName + SECTION_SUFFIX);
    28                 }
    29             } else {
    30                 //normal line - add it to the existing content buffer:
    31                 sectionContent.append(rawLine).append("
    ");
    32             }
    33         }
    34 
    35         //finish any remaining buffered content:
    36         addSection(sectionName, sectionContent);
    37     }

    这个方法就是一行一行读取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)。

    1 public static class Section implements Map<String, String> {
    2         private final String name;
    3         private final Map<String, String> props;
    4         ...............      
    5 }

    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中。

     1         private static Map<String, String> toMapProps(String content) {
     2             Map<String, String> props = new LinkedHashMap<String, String>();
     3             String line;
     4             StringBuilder lineBuffer = new StringBuilder();
     5             Scanner scanner = new Scanner(content);
     6             while (scanner.hasNextLine()) {
     7                 line = StringUtils.clean(scanner.nextLine());
     8                 if (isContinued(line)) {
     9                     //strip off the last continuation backslash:
    10                     line = line.substring(0, line.length() - 1);
    11                     lineBuffer.append(line);
    12                     continue;
    13                 } else {
    14                     lineBuffer.append(line);
    15                 }
    16                 line = lineBuffer.toString();
    17                 lineBuffer = new StringBuilder();
    18                 String[] kvPair = splitKeyValue(line);
    19                 props.put(kvPair[0], kvPair[1]);
    20             }
    21 
    22             return props;
    23         }

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

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

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

    isContinued

     1  protected static boolean isContinued(String line) {
     2             if (!StringUtils.hasText(line)) {
     3                 return false;
     4             }
     5             int length = line.length();
     6             //find the number of backslashes at the end of the line.  If an even number, the
     7             //backslashes are considered escaped.  If an odd number, the line is considered continued on the next line
     8             int backslashCount = 0;
     9             for (int i = length - 1; i > 0; i--) {
    10                 if (line.charAt(i) == ESCAPE_TOKEN) {
    11                     backslashCount++;
    12                 } else {
    13                     break;
    14                 }
    15             }
    16             return backslashCount % 2 != 0;
    17         }

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

    1 security
    2 Manager.realms=$myRealm1,$myRealm3

    1 securityManager.realms=$myRealm1,$myRealm3

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

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

    splitKeyValue

     1  protected static String[] splitKeyValue(String keyValueLine) {
     2             String line = StringUtils.clean(keyValueLine);
     3             if (line == null) {
     4                 return null;
     5             }
     6             StringBuilder keyBuffer = new StringBuilder();
     7             StringBuilder valueBuffer = new StringBuilder();
     8 
     9             boolean buildingKey = true; //we'll build the value next:
    10 
    11             for (int i = 0; i < line.length(); i++) {
    12                 char c = line.charAt(i);
    13 
    14                 if (buildingKey) {
    15                     if (isKeyValueSeparatorChar(c) && !isCharEscaped(line, i)) {
    16                         buildingKey = false;//now start building the value
    17                     } else {
    18                         keyBuffer.append(c);
    19                     }
    20                 } else {
    21                     if (valueBuffer.length() == 0 && isKeyValueSeparatorChar(c) && !isCharEscaped(line, i)) {
    22                         //swallow the separator chars before we start building the value
    23                     } else {
    24                         valueBuffer.append(c);
    25                     }
    26                 }
    27             }
    28 
    29             String key = StringUtils.clean(keyBuffer.toString());
    30             String value = StringUtils.clean(valueBuffer.toString());
    31 
    32             if (key == null || value == null) {
    33                 String msg = "Line argument must contain a key and a value.  Only one string token was found.";
    34                 throw new IllegalArgumentException(msg);
    35             }
    36 
    37             log.trace("Discovered key/value pair: {}={}", key, value);
    38 
    39             return new String[]{key, value};
    40         }

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

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

  • 相关阅读:
    用python3实现AES/CBC/PKCS5padding算法加解密
    python之逆向某贷款app破解sign参数
    用Python实现RSA签名和验签
    python3 RSA 长字符串分段加密解密
    PyCharm 字体大小颜色常用功能设置
    pycharm2019.3/pycharm2020.2 专业版 安装教程永久激活
    Android 四大组件和Intent
    Linux的查找命令
    Linux(centos)系统各个目录的作用详解
    linux ls文件颜色和底色设置
  • 原文地址:https://www.cnblogs.com/abcwt112/p/4641500.html
Copyright © 2011-2022 走看看