@

  上篇文章Shiro源码分析之获取SecurityManager工厂获取我们介绍了SecurityManager工厂的获取步骤,本文在此基础上来分析下SecurityManager对象产生的过程。

SecurityManager获取过程

1.SecurityManager接口介绍

  SecurityManager安全管理器,是Shiro的核心,继承了三个接口,其定义的方法如下

public interface SecurityManager extends Authenticator, Authorizer, SessionManager {
// 登录
Subject login(Subject subject, AuthenticationToken authenticationToken) throws AuthenticationException;
// 注销
void logout(Subject subject);
// 获取Subject对象
Subject createSubject(SubjectContext context);
}

本文重点不是分析SecurityManager的结构,所以此处略过。

2.SecurityManager实例化时序图

3.源码分析

3.1.AbstractFactory

public T getInstance() {
T instance;
// 判断是否是单例,默认就是单例
if (isSingleton()) {
if (this.singletonInstance == null) {
// 创建实例
this.singletonInstance = createInstance();
}
instance = this.singletonInstance;
} else {
instance = createInstance();
}
if (instance == null) {
String msg = "Factory 'createInstance' implementation returned a null object.";
throw new IllegalStateException(msg);
}
return instance;
}
// 模板模式 定义抽象方法,交给子类实现
protected abstract T createInstance();

3.2 IniFactorySupport

public T createInstance() {
// 获取Ini对象 该对象在获取SecurityManager工厂的时候被实例化了
Ini ini = resolveIni(); T instance; if (CollectionUtils.isEmpty(ini)) {
log.debug("No populated Ini available. Creating a default instance.");
instance = createDefaultInstance();
if (instance == null) {
String msg = getClass().getName() + " implementation did not return a default instance in " +
"the event of a null/empty Ini configuration. This is required to support the " +
"Factory interface. Please check your implementation.";
throw new IllegalStateException(msg);
}
} else {
log.debug("Creating instance from Ini [" + ini + "]");
// 创建实例
instance = createInstance(ini);
if (instance == null) {
String msg = getClass().getName() + " implementation did not return a constructed instance from " +
"the createInstance(Ini) method implementation.";
throw new IllegalStateException(msg);
}
} return instance;
}
// 抽象方法 进入子类中查看
protected abstract T createInstance(Ini ini);

3.3 IniSecurityManagerFactory

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;
} private SecurityManager createSecurityManager(Ini ini) {
// 获取Ini中保存的shiro.ini文件中的section的 main信息
Ini.Section mainSection = ini.getSection(MAIN_SECTION_NAME);
if (CollectionUtils.isEmpty(mainSection)) {
//try the default: 默认是 ""
mainSection = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
return createSecurityManager(ini, mainSection);
}

3.4 进入createSecurityManager方法

private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {
// 创建默认的 SecurityManager和IniRealm 跳转到3.4.1
Map<String, ?> defaults = createDefaults(ini, mainSection);
// 绑定对象到SecurityManager对象
Map<String, ?> objects = buildInstances(mainSection, defaults);
// 从objects 中获取SecurityManager对象
SecurityManager securityManager = getSecurityManagerBean();
// 判断是否自动应用realm
boolean autoApplyRealms = isAutoApplyRealms(securityManager);
// 因为我们在shiro.ini中配置了users所以前面创建了IniRealm所以为true
if (autoApplyRealms) {
//realms and realm factory might have been created - pull them out first so we can
//initialize the securityManager:
Collection<Realm> realms = getRealms(objects);
//set them on the SecurityManager
if (!CollectionUtils.isEmpty(realms)) {
// 将IniRealm绑定到了SecurityManager中
applyRealmsToSecurityManager(realms, securityManager);
}
}
// 初始化Realm 跳至 3.5处
initRealms(securityManager); return securityManager;
}

3.4.1 此处需要进入createDefaults查看

protected Map<String, ?> createDefaults(Ini ini, Ini.Section mainSection) {
Map<String, Object> defaults = new LinkedHashMap<String, Object>();
// 此处的代码 实例化了SecurityManager 对象
// 3.4.2进入createDefaultInstance查看
SecurityManager securityManager = createDefaultInstance();
defaults.put(SECURITY_MANAGER_NAME, securityManager);
// 判断ini中是否隐含的有realm 3.4.3查看
if (shouldImplicitlyCreateRealm(ini)) {
// 创建realm 查看createRealm方法 3.4.4查看
Realm realm = createRealm(ini);
if (realm != null) {
defaults.put(INI_REALM_NAME, realm);
}
} return defaults;
}

3.4.2 createDefaultInstance方法

protected SecurityManager createDefaultInstance() {
// SecurityManager模式的实现是DefaultSecurityManager实例
return new DefaultSecurityManager();
}

3.4.3 shouldImplicitlyCreateRealm方法

protected boolean shouldImplicitlyCreateRealm(Ini ini) {
// 返回结果的判断条件是 ini不为空同时(ini中包含roles或者users)就为true
return !CollectionUtils.isEmpty(ini) &&
(!CollectionUtils.isEmpty(ini.getSection(IniRealm.ROLES_SECTION_NAME)) ||
!CollectionUtils.isEmpty(ini.getSection(IniRealm.USERS_SECTION_NAME)));
}

3.4.4 createRealm

 protected Realm createRealm(Ini ini) {
// 我们可以看到实例化的是IniRealm对象
IniRealm realm = new IniRealm(ini);
realm.setName(INI_REALM_NAME);
return realm;
}

3.5 initRealms

private void initRealms(SecurityManager securityManager) {
Collection<Realm> realms = getRealms(securityManager);
if (!CollectionUtils.isEmpty(realms)) {
LifecycleUtils.init(realms);
}
}

3.6 LifecycleUtils

public static void init(Collection c) throws ShiroException {
if (c == null || c.isEmpty()) {
return;
}
for (Object o : c) {
init(o);
}
} public static void init(Object o) throws ShiroException {
if (o instanceof Initializable) {
init((Initializable) o);
}
} public static void init(Initializable initializable) throws ShiroException {
initializable.init();
}

3.7AuthorizingRealm

public final void init() {
//trigger obtaining the authorization cache if possible
getAvailableAuthorizationCache();
onInit(); // 进入IniRealm中
}

3.8 IniRealm

protected void onInit() {
// This is an in-memory realm only - no need for an additional cache when we're already
// as memory-efficient as we can be.
String resourcePath = getResourcePath(); if (CollectionUtils.isEmpty(this.users) && CollectionUtils.isEmpty(this.roles)) {
//no account data manually populated - try the resource path:
if (StringUtils.hasText(resourcePath)) {
log.debug("Resource path {} defined. Creating INI instance.", resourcePath);
Ini ini = Ini.fromResourcePath(resourcePath);
// 核心方法进入
processDefinitions(ini);
} else {
throw new IllegalStateException("No resource path was specified. Cannot load account data.");
}
} else {
if (StringUtils.hasText(resourcePath)) {
log.warn("Users or Roles are already populated. Resource path property will be ignored.");
}
}
}

processDefinitions

private void processDefinitions(Ini ini) {
if (CollectionUtils.isEmpty(ini)) {
log.warn("{} defined, but the ini instance is null or empty.", getClass().getSimpleName());
return;
} Ini.Section rolesSection = ini.getSection(ROLES_SECTION_NAME);
if (!CollectionUtils.isEmpty(rolesSection)) {
log.debug("Discovered the [{}] section. Processing...", ROLES_SECTION_NAME);
// 处理角色信息
processRoleDefinitions(rolesSection);
} Ini.Section usersSection = ini.getSection(USERS_SECTION_NAME);
if (!CollectionUtils.isEmpty(usersSection)) {
log.debug("Discovered the [{}] section. Processing...", USERS_SECTION_NAME);
// 处理用户信息
processUserDefinitions(usersSection);
} else {
log.info("{} defined, but there is no [{}] section defined. This realm will not be populated with any " +
"users and it is assumed that they will be populated programatically. Users must be defined " +
"for this Realm instance to be useful.", getClass().getSimpleName(), USERS_SECTION_NAME);
}
}

3.9 TextConfigurationRealm

processRoleDefinitions

protected void processRoleDefinitions(Map<String, String> roleDefs) {
if (roleDefs == null || roleDefs.isEmpty()) {
return;
} for (String rolename : roleDefs.keySet()) {
String value = roleDefs.get(rolename); SimpleRole role = getRole(rolename);
if (role == null) {
role = new SimpleRole(rolename);
// 添加角色
add(role);
} Set<Permission> permissions = PermissionUtils.resolveDelimitedPermissions(value, getPermissionResolver());
// 添加权限
role.setPermissions(permissions);
}
}

processUserDefinitions

protected void processUserDefinitions(Map<String, String> userDefs) {
if (userDefs == null || userDefs.isEmpty()) {
return;
} for (String username : userDefs.keySet()) { String value = userDefs.get(username); String[] passwordAndRolesArray = StringUtils.split(value); String password = passwordAndRolesArray[0]; SimpleAccount account = getUser(username);
if (account == null) {
account = new SimpleAccount(username, password, getName());
//添加账号
add(account);
}
account.setCredentials(password); if (passwordAndRolesArray.length > 1) {
for (int i = 1; i < passwordAndRolesArray.length; i++) {
String rolename = passwordAndRolesArray[i];
account.addRole(rolename); SimpleRole role = getRole(rolename);
if (role != null) {
account.addObjectPermissions(role.getPermissions());
}
}
} else {
account.setRoles(null);
}
}
}

3.10 SimpleAccountRealm

protected void add(SimpleRole role) {
roles.put(role.getName(), role);
}
 protected void add(SimpleAccount account) {
String username = getUsername(account);
this.users.put(username, account);
}

最后回到createSecurityManager方法中

private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {
// 创建默认的 SecurityManager和IniRealm
Map<String, ?> defaults = createDefaults(ini, mainSection);
// 绑定对象到SecurityManager对象
Map<String, ?> objects = buildInstances(mainSection, defaults);
// 从objects 中获取SecurityManager对象
SecurityManager securityManager = getSecurityManagerBean();
// 判断是否自动应用realm
boolean autoApplyRealms = isAutoApplyRealms(securityManager);
// 因为我们在shiro.ini中配置了users所以前面创建了IniRealm所以为true
if (autoApplyRealms) {
//realms and realm factory might have been created - pull them out first so we can
//initialize the securityManager:
Collection<Realm> realms = getRealms(objects);
//set them on the SecurityManager
if (!CollectionUtils.isEmpty(realms)) {
// 将IniRealm绑定到了SecurityManager中
applyRealmsToSecurityManager(realms, securityManager);
}
}
// 初始化Realm
initRealms(securityManager); return securityManager;
}

初始化SecurityManager完成

4.总结

  1. SecurityManager默认实例的是DefaultSecurityManager
  2. 如果我们在shiro.ini配置文件配置了[Users]的话那么会自动创建IniRealm
  3. 创建的IniRealm会被绑定到SecurityManager对象中,并且会将账号密码保存到SimpleAccountRealm的User集合中,认证的时候会从此对象中获取

Shiro源码分析之SecurityManager对象获取的更多相关文章

  1. Shiro 源码分析

    http://my.oschina.net/huangyong/blog/215153 Shiro 是一个非常优秀的开源项目,源码非常值得学习与研究. 我想尝试做一次 不一样 的源码分析:源码分析不再 ...

  2. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  3. Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号

    Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...

  4. JVM源码分析之Java对象头实现

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Java对象头实现 HotSpot虚拟机中,对象在内存中的布局分为三 ...

  5. Shiro源码分析

    1.入口类:AbstractAuthenticator 用户输入的登录信息经过其authenticate方法: public final AuthenticationInfo authenticate ...

  6. [旧][Android] Retrofit 源码分析之 ServiceMethod 对象

    备注 原发表于2016.05.03,资料已过时,仅作备份,谨慎参考 前言 大家好,我又来学习 Retrofit 了,可能这是最后一篇关于 Retrofit 框架的文章了.我发现源码分析这回事,当时看明 ...

  7. HotSpot源码分析之C++对象的内存布局

    HotSpot采用了OOP-Klass模型来描述Java类和对象.OOP(Ordinary Object Pointer)指的是普通对象指针,而Klass用来描述对象的具体类型.为了更好理解这个模型, ...

  8. Spring源码分析(五)获取Document

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 这一篇开始进行Document加载了,XmlBeanFactoryRea ...

  9. Springboot源码分析之代理对象内嵌调用

    摘要: 关于这个话题可能最多的是@Async和@Transactional一起混用,我先解释一下什么是代理对象内嵌调用,指的是一个代理方法调用了同类的另一个代理方法.首先在这儿我要声明事务直接的嵌套调 ...

随机推荐

  1. MD5加密过时方法替换

    使用System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile进行MD5加密时,会出现已过时 /// < ...

  2. python语法_模块_re(正则表达)

    字符串操作方法有: s = 'hello word' s.find('l') 查询第一个l的索引 s.replace('ll','xx') 替换 s.split('w') 以w进行分割 这些都是完全匹 ...

  3. [译文]Domain Driven Design Reference(六)—— 提炼战略设计

    本书是Eric Evans对他自己写的<领域驱动设计-软件核心复杂性应对之道>的一本字典式的参考书,可用于快速查找<领域驱动设计>中的诸多概念及其简明解释. 其它本系列其它文章 ...

  4. 别以为真懂Openstack: 虚拟机创建的50个步骤和100个知识点(1)

    还是先上图吧,无图无真相 别以为真懂Openstack!先别着急骂我,我也没有说我真懂Openstack 我其实很想弄懂Openstack,然而从哪里下手呢?作为程序员,第一个想法当然是代码,Code ...

  5. 对JS闭包和函数作用域的问题的深入讨论,如何理解JS闭包和函数作用域链?

    首先先引用<JavaScript权威指南>里面的一句话来开始我的博客:函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的. 因此,就出现了如下的几串代码: ...

  6. netsh winsock reset命令

    公司一台电脑无法浏览网页,其他基本正常,鼓捣了一个多小时,依然无法解决.. 一开始按照正常思路,感觉是dns的问题,查看了下DNS,真是自定义的,于是改成自动获取,无效 重启了网卡,无效 重启电脑,无 ...

  7. 关于video标签移动端开发遇到的问题,获取视频第一帧,全屏,自动播放,自适应等问题

    最近一直在处理video标签在IOS和Android端的兼容问题,其中遇到不少坑,绝大多数问题已经解决,下面是处理问题经验的总结: 1.获取视频的第一帧作为背景图: 技术:canvas绘图 windo ...

  8. javascript 使用小技巧总结

    按位取反 ~a 即:返回 -(a+1),会去掉小数点. let a = 3.14; let b = ~a; //b = -(3.14+1) 取整 为-4: let c = ~b; //c = -(-4 ...

  9. 【转】ret,retf,iret的区别

    ret RET, and its exact synonym RETN, pop IP or EIP from the stack and transfer control to the new ad ...

  10. 『最短Hamilton路径 状态压缩DP』

    状压DP入门 最短Hamilton路径 Description 给定一张 n(n≤20) 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径. Hamil ...