前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Shiro源码分析之获取SecurityManager工厂获取

Shiro源码分析之获取SecurityManager工厂获取

作者头像
用户4919348
发布2019-04-02 11:07:56
9360
发布2019-04-02 11:07:56
举报
文章被收录于专栏:波波烤鸭

  最近在介绍Shiro所以打算将shiro中的相关源代码给分析下。因为内容比较多,所以会分几篇来介绍,希望对大家有所帮助。

shiro源码分析

1.分析数据准备

  先介绍下数据环境,后面的分析如果没有特别说明就都是基于此环境来测试的。

代码语言:javascript
复制
<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-core</artifactId>
	<version>1.1.0</version>
</dependency>
<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-simple</artifactId>
	<version>1.6.1</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.12</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>

shiro.ini文件

代码语言:javascript
复制
[users]
root = 123456
# 账号:root  密码:123456

测试代码

代码语言:javascript
复制
public static void main(String[] args) {
	// 注意:SecurityManager在java.lang包下也有同名的SecurityManager
	// 1.获取SecurityManager工厂对象 同时将配置文件信息封装到了ini对象中
	Factory<SecurityManager> factory = 
			new IniSecurityManagerFactory("classpath:shiro.ini");
	// 2.获取SecurityManager对象
	SecurityManager securityManager = factory.getInstance();
	// 3.将SecurityManager添加到当前的运行环境中
	SecurityUtils.setSecurityManager(securityManager);
	
	// 4.获取subject
	Subject subject = SecurityUtils.getSubject();
	AuthenticationToken token = new UsernamePasswordToken("admin", "12345");
	// 做登录认证
	try {
		subject.login(token );
	} catch (UnknownAccountException e) {
		System.out.println("账号不存在...");
	} catch (IncorrectCredentialsException e) {
		System.out.println("密码错误...");
	}
	
	System.out.println(subject.isAuthenticated());
}

2.获取SecurityManager工厂对象分析

  我们先来分析下SecurityManager工厂对象的源代码,来看看具体做了什么事情

代码语言:javascript
复制
// 1.获取SecurityManager工厂对象 同时将配置文件信息封装到了ini对象中
Factory<SecurityManager> factory = 
		new IniSecurityManagerFactory("classpath:shiro.ini");

2.1先来看下对应的时序图

在这里插入图片描述
在这里插入图片描述

2.2 源码跟踪

进入IniSecurityManagerFactory有参构造方法中查看。

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
public IniSecurityManagerFactory(String iniResourcePath) {
    // 1.先执行Ini中的fromResourcePath方法加载配置文件
    // 2.调用自身另一个有参构造方法
    this(Ini.fromResourcePath(iniResourcePath));
}

进入fromResourcePath方法查看

代码语言:javascript
复制
// 方法的返回值是 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 ini = new Ini();
    // Ini对象通过loadFromPath加载配置文件
    ini.loadFromPath(resourcePath);
    return ini;
}

进入loadFromPath方法

代码语言:javascript
复制
public void loadFromPath(String resourcePath) throws ConfigurationException {
    InputStream is;
    try {
    	// 将配置文件加载到内存中 以字节输入流的方式
        is = ResourceUtils.getInputStreamForPath(resourcePath);
    } catch (IOException e) {
        throw new ConfigurationException(e);
    }
    // 继续
    load(is);
}

继续进入load方法

代码语言:javascript
复制
public void load(InputStream is) throws ConfigurationException {
    if (is == null) {
        throw new NullPointerException("InputStream argument cannot be null.");
    }
    InputStreamReader isr;
    try {
    	// 将InputStream包装成了InputStreamReader
        isr = new InputStreamReader(is, DEFAULT_CHARSET_NAME);
    } catch (UnsupportedEncodingException e) {
        throw new ConfigurationException(e);
    }
    // 继续进入
    load(isr);
}

继续进入load

代码语言:javascript
复制
public void load(Reader reader) {
    Scanner scanner = new Scanner(reader);
    try {
        // 继续进入
        load(scanner);
    } finally {
        try {
            scanner.close();
        } catch (Exception e) {
            log.debug("Unable to cleanly close the InputStream scanner.  Non-critical - ignoring.", e);
        }
    }
}
代码语言:javascript
复制
public void load(Scanner scanner) {
   // 存储 sectionName 默认是""
   String sectionName = DEFAULT_SECTION_NAME;
   // sectionContent 拼接section的内容
    StringBuilder sectionContent = new StringBuilder();

    while (scanner.hasNextLine()) {
		// 循环读取的每行记录
        String rawLine = scanner.nextLine();
        // 去掉两侧的空格
        String line = StringUtils.clean(rawLine);
		// null 或者 #开头 ;开头就跳过
        if (line == null || line.startsWith(COMMENT_POUND) || line.startsWith(COMMENT_SEMICOLON)) {
            //skip empty lines and comments:
            continue;
        }
		// [users]截取掉 [和]  具体实现看后面的方法
        String newSectionName = getSectionName(line);
        if (newSectionName != null) {
            //found a new section - convert the currently buffered one into a Section object
            // 将获取的新的section(ini中的门面关键字)
            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);
}

getSectionName方法

代码语言:javascript
复制
protected static String getSectionName(String line) {
    String s = StringUtils.clean(line);
    
    if (isSectionHeader(s)) {
        // 去掉第一个和最后一个字符
        return cleanName(s.substring(1, s.length() - 1));
    }
    return null;
}
代码语言:javascript
复制
protected static boolean isSectionHeader(String line) {
    String s = StringUtils.clean(line);
    // 记录不为空 [开头 ]结尾就返回true否则false
    return s != null && s.startsWith(SECTION_PREFIX) && s.endsWith(SECTION_SUFFIX);
}

进入addSection方法

代码语言:javascript
复制
private void addSection(String name, StringBuilder content) {
    if (content.length() > 0) {
        String contentString = content.toString();
        String cleaned = StringUtils.clean(contentString);
        if (cleaned != null) {
            Section section = new Section(name, contentString);
            if (!section.isEmpty()) {
            	// 该代码是核心
                sections.put(name, section);
            }
        }
    }
}
在这里插入图片描述
在这里插入图片描述

好了配置文件的加载到此,回到this(Ini.fromResourcePath(iniResourcePath))这儿来,Ini.fromResourcePath(iniResourcePath)代码返回了Ini对象,同时解析了shiro.ini中的配置信息,并保存在了Ini的sections对象中。 接下来进入this(Ini.fromResourcePath(iniResourcePath))构造方法中来看。

代码语言:javascript
复制
public IniSecurityManagerFactory(Ini config) {
    setIni(config);
}

进入setIni方法

在这里插入图片描述
在这里插入图片描述

将Ini赋值给了IniFactorySupport的成员变量,至此代码看完,

2.3 总结

  1. SecurityManager工厂对象通过IniSecurityManagerFactory实例获取
  2. 获取过程中加载了配置文件,并将section信息保存在了Ini对象
  3. IniFactorySupport对象持有Ini对象的引用。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年03月08日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • shiro源码分析
    • 1.分析数据准备
      • 2.获取SecurityManager工厂对象分析
        • 2.1先来看下对应的时序图
        • 2.2 源码跟踪
        • 2.3 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档