Shiro,一个易用的Java安全框架,主要集合身份认证、授权、加密和session管理的功能。

这系文章主要简介Shiro架构,并通过官方的quickstart例程分析最简实现下Shiro的工作流程及其源码

(1-初始化)主要由从零到获取一个subject

本文使用的是shiro 1.3.2版本,配合源码食用更佳~

它的架构如下

Subject:与框架交互的实体(Entity)的安全视图(security-specific ‘view’),这个实体可以是用户、第三方应用、定时任务(cron job)等

SecurityManager:是Shiro的框架核心,协调设置内部组件对外提供统一的对象视图,一旦Shiro对外提供Subject后外部基本上只需要对Subject进行交互,而不需要管理内部机制。在简化的概念下调用者只需关心Subject、SecurityManager、Realm三者就可以轻松地使用shiro ,这体现了其极好的封装性。

Realms:上面谈到的Realm是Shiro连接应用安全信息数据(即用户的密码、权限等信息)的组件,类似于DAO层,Realms自身提供JDBC、LDAP、文本等Realm实现,也可以根据需要继承对应接口实现自定义数据源

Authenticator:用户身份验证组件,用户尝试登陆时,会通过Authenticator进行验证,它调用Realms并通过一定策略(Authentication Strategy)来确认用户身份的真实性。

Authorizer:在确认用户登录后,权限验证的组件。同样它会调用Realm获得安全数据来判断用户对资源的权限。

SessionManager:shiro自身维护了一套session管理组件,它并不单纯依赖WEB/Servlet/EJB容器等环境,即它可以独立使用,在WEB环境下或者普通应用环境下,可以理解为将session概念复用到了一般应用环境。另外,组件还可拔插地支持SessionDAO实现session的持久化及应用的分布式,外接mysql或redis等数据库

CacheManager:缓存方面提供了CacheManager、Cache、CacheManagerAware三个接口和一个基于内存的实现类MemoryConstrainedCacheManager,可以自己实现类来实现其他诸如Hazelcast, Ehcache, OSCache, Terracotta的支持,官方可能有前两个缓存的支持计划(官方文档显示TBD~~)。需要注意的是,在使用缓存情况下,改变User的安全权限后务必使用clearCachedAuthorizationInfo方法删除现用缓存以避免不同步的情况。

Cryptography:加密支持组件。shiro提供了便携的加密算法类实现一般的哈希功能,可用在用身份确认时来源密码hash与realms中数据校验

以上是Shiro的架构概况,下面结合一个官方demo来阐述Shiro运行流程

1-首先是获得Subject的过程

1.1首先,获得一个产生SecurityManager工厂

Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

IniSecurityManagerFactory的参数是ini文件的路径,它是一个支持ini文件作为realms数据源的SecurityManager工厂类

SecurityManager的继承关系如下,AbstractFactory则实现了Factory接口

在构建工厂实例时,使用了构造器IniSecurityManagerFactory(String iniResourcePath),其中调用了以下构造器

public IniSecurityManagerFactory(Ini config) {
this();
setIni(config);
}

其具体方法在IniFactorySupport中实现

public Ini getIni() {
return ini;
}

1.2获得 SecurityManager实例

SecurityManager securityManager = factory.getInstance();

其工厂的接口方法在AbstractFactory中重写,由下可见securityManager在程序中是以单例存在

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;
}

其中createInstance是抽象方法

protected abstract T createInstance();

其在IniFactorySupport类中实现

public T createInstance() {
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;
}

其中createInstance为抽象方法在IniSecurityManagerFactory中实现

protected SecurityManager createInstance(Ini ini) {
if (CollectionUtils.isEmpty(ini)) {
throw new NullPointerException("Ini argument cannot be null or empty.");
}
SecurityManager securityManager = createSecurityManager(ini);
if (securityManager == null) {
String msg = SecurityManager.class + " instance cannot be null.";
throw new ConfigurationException(msg);
}
return securityManager;
}

里面createSecurityManager(ini)方法又调用了一下方法传入的Ini.Section mainSection是在createSecurityManager(ini)方法中获得的ini文件的主节点,这里看到了下一个关键组件 Realms

private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {

        Map<String, ?> defaults = createDefaults(ini, mainSection);
Map<String, ?> objects = buildInstances(mainSection, defaults); SecurityManager securityManager = getSecurityManagerBean(); boolean autoApplyRealms = isAutoApplyRealms(securityManager); 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)) {
applyRealmsToSecurityManager(realms, securityManager);
}
} return securityManager;
}

这里详细说明一下createSecurityManager内的过程

1.2.1:根据ini返回含DefaultSecurityManager和IniRealm的map

1.2.2:将节点信息转化为bean 调用LifecycleUtils初始化cache等

1.2.3:getSecurityManagerBean调用builder.getBean获取objects中key为securityManager的Bean

1.2.4:判断是否AutoApply的SecurityManager 实体(未被其他user设置过)

1.2.5:没有被设置过则进入流程,如果被设置过则直接return

1.2.6:从object中获取realm实例,这里可以看出shiro支持多个realm。从objects中获取realm

1.2.7:判断realms是否为空,如果是空则直接return securityManager ,如果不是的话则继续流程

1.2.8:将realms推至securityManager 。这里赋予除了还将CacheManager,EventBus设置到realm

1.3获取Subject

SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();

调用SecurityUtils.setSecurityManager 设置 securityManager,调用getSubjext

首先确认线程中是否有绑定subject,有则直接return对应subject ,无则进入获取流程,并绑定到线程再return

public static Subject getSubject() {
Subject subject = ThreadContext.getSubject();
if (subject == null) {
subject = (new Subject.Builder()).buildSubject();
ThreadContext.bind(subject);
}
return subject;
}

用内部类Subject.Builder()构建,调用设置SecurityManager

this(SecurityUtils.getSecurityManager());

调用DefaultSecurityManager.createSubject  初始化确认Subject中的信息,然后在session中保存,如果subject开启了持久化则会实现持久化(调用subjectDAO.save很有迷惑性~

public Subject createSubject(SubjectContext subjectContext) {
//create a copy so we don't modify the argument's backing map:
SubjectContext context = copy(subjectContext); //ensure that the context has a SecurityManager instance, and if not, add one:
context = ensureSecurityManager(context); //Resolve an associated Session (usually based on a referenced session ID), and place it in the context before
//sending to the SubjectFactory. The SubjectFactory should not need to know how to acquire sessions as the
//process is often environment specific - better to shield the SF from these details:
context = resolveSession(context); //Similarly, the SubjectFactory should not require any concept of RememberMe - translate that here first
//if possible before handing off to the SubjectFactory:
context = resolvePrincipals(context); Subject subject = doCreateSubject(context); //save this subject for future reference if necessary:
//(this is needed here in case rememberMe principals were resolved and they need to be stored in the
//session, so we don't constantly rehydrate the rememberMe PrincipalCollection on every operation).
//Added in 1.2:
save(subject); return subject;
}

然后是返回subject

至此初始化工作完成,并获得了对外的实例subject后续只要操作Suject API即可

参考:

http://shiro.apache.org/10-minute-tutorial.html

http://shiro.apache.org/architecture.html

http://www.apache.org/dyn/closer.cgi/shiro/1.3.2/shiro-root-1.3.2-source-release.zip

转载请注明作者及来源:https://www.cnblogs.com/codflow/

Shiro官方快速入门10min例子源码解析框架1-初始化的更多相关文章

  1. Shiro官方快速入门10min例子源码解析框架2-Session

    Shiro自身维护了一套session管理组件,它可以独立使用,并不单纯依赖WEB/Servlet/EJB容器等环境,使得它的session可以任何应用中使用. 2-Session)主要介绍在quic ...

  2. Shiro官方快速入门10min例子源码解析框架3-Authentication(身份认证)

    在作完预备的初始化和session测试后,到了作为一个权鉴别框架的核心功能部分,确认你是谁--身份认证(Authentication). 通过提交给shiro身份信息来验证是否与储存的安全信息数据是否 ...

  3. Orleans例子源码

    这是Orleans系列文章中的一篇.首篇文章在此 我共享以下我现在写教程的简单的Orleans例子源码. 这个代码已经是我为了写word改动过了的.不过大体内容是通用的. 我写博客总体想法是:去除所有 ...

  4. 【Activiti工作流引擎】官方快速入门demo

    Activiti官方快速入门demo 地址: https://www.activiti.org/quick-start 0. 版本 activiti 5.22.0 JDK 1.8 1. 介绍 这个快速 ...

  5. 分布式 PostgreSQL 集群(Citus),官方快速入门教程

    多租户应用程序 在本教程中,我们将使用示例广告分析数据集来演示如何使用 Citus 来支持您的多租户应用程序. 注意 本教程假设您已经安装并运行了 Citus. 如果您没有运行 Citus,则可以使用 ...

  6. 使用C#类向数据库添加数据的例子源码

    在上一篇中,增加了sql server数据库操作类SqlOperator,用于操作sql server数据库.还有一个SqlStringHelper类,用于处理sql语句的单引号.那么这两个类怎么使用 ...

  7. jackson官方快速入门文档

    官方地址: http://jackson.codehaus.org/ http://wiki.fasterxml.com/JacksonInFiveMinutes http://wiki.faster ...

  8. TodoMVC中的Backbone+MarionetteJS+RequireJS例子源码分析之一

    Marionette牵线木偶,Backbone是脊骨的意思,Marionette是基于Backbone做扩展库,可以理解为把脊骨骨架绑线扯着变成牵线木偶动起来哈哈,使backbone更易使用呵呵! 构 ...

  9. Android例子源码非第三方实现根据字母排序的城市列表

    values 下dimens.xml <resources> <!-- Default screen margins, per the Android Design guidelin ...

随机推荐

  1. 【招聘】滴滴滴~ i春秋内推直通车来咯,帮你找工作!

    凑是这么简单粗暴,i春秋冬日特享福利!虽然金九银十已经过去,但素想换工作想找工作的小哥哥小姐姐看过来! [职位方向]渗 透 测 试.代 码 审 计.安全开发.病毒分析.风险控制.安全运维.....任何 ...

  2. Kali Linux来袭~老司机带你进击

    Kali是BackTrackLinux完全遵循Debian开发标准彻底的完全重建.全新的目录框架,复查并打包所有工具,我们还为VCS建立了Git树. 本次推荐内容主要介绍Kali-Linux的安装,包 ...

  3. 磁盘IO的概念

    转载自:http://blog.csdn.net/letterwuyu/article/details/53542291 在数据库优化和存储规划过程中,总会提到IO的一些重要概念,在这里就详细记录一下 ...

  4. 考试题 T3

    题意分析 首先\(\%\%\%\%olinr\)以及花_Q\(julao\)当场切题 然后就是怎么求 \[max(|a-A|,|b-B|)=max(a-A,A-a,B-b,b-B)\] 我们令\(x_ ...

  5. 状态机模式中的Task与对象池

    Task 抽象带来Task 首先,假设我们有这么一段逻辑:收到一个参数,先校验格式是否正确,再提取相关的参数出来,执行我们的事务,然后构建结果并返回.伪代码如下: /** * 一个engine类 ** ...

  6. hiho# 1394最小路径覆盖 网络流拆点

    题目传送门 思路: 观察到路径上除了终点起点以外的每个点出度和入度都为1,和网络流的拆点很像,所以就把每个点都拆成两个点,若存在一条路径$(u,v)$,则建一条$(u,v+n,1)$的边,然后求出最大 ...

  7. 插播一条 QQ头像无法正常显示问题

    问题背景 不知道啥什么,QQ群的头像有些显示不全直接是默认的头像.想一想最近也没做啥,怎么就出问题了. 后来想一想,大概是个人文件夹的文件出问题了 解决办法 好友头像显示问题的删除 MiscHead. ...

  8. Secondary NameNode究竟是做什么的

    Secondary NameNode:它究竟有什么作用? 在hadoop中,有一些命名不好的模块,Secondary NameNode是其中之一.从它的名字上看,它给人的感觉就像是NameNode的备 ...

  9. 【实战】Tomcat管理后台Getshell

    一.制作war包 1.xiaoma.jsp压缩成xiaoma.zip格式,然后修改为xiaoma.war 2.修仙之百度大法 二.部署war包(选择文件,上传即可,不啰嗦啦) 三.C刀连接:http: ...

  10. 【Guava】Optional接口来避免空指针错误

    null会带来很多问题,从开始有null开始有无数程序栽在null的手里,null的含义是不清晰的,检查null在大多数情况下是不得不做的,而我们又在很多时候忘记了对null做检查,在我们的产品真正投 ...