Apache Shiro的设计目标是通过直观和易用来简化应用安全的使用.大多数人在和应用交互时思考应用安全的流程是一致的,Shiro的核心设计思想就是那个流程的具体化.
软件应用通常被设计为基于用户的操作环境.也就是说,你通常会根据用户如何与软件交互来设计用户界面和服务API.比如,你或许会说"如果和我的应用交互的用户已登录,我就展示一个点击后可以显示账户信息的按钮,如果没有登录就显示一个登录按钮".
这个案例表明编写应用程序主要是为了满足用户的需求和目标,即使用户是另一个软件系统或其它可交互的软件,你仍然可以根据当前交互的行为来编写代码.
Shiro在自己的设计中反映了这些概念,通过匹配对软件开发人员来说已经很直观的东西,ApacheShiro保持了直观性,并且在几乎任何应用程序中都很容易使用.
最高级预览
在最高级别的概念层级上,Shiro的架构主要有3个概念:
Subject
,SecurityManager
和Realms
.下面的图是一个关于组件如何交互的统筹概览,我们将在接下来逐一介绍每个组件:-
Subject:正如我们在入门教程中提到的那样,
Subject
在本质上是当前执行用户的实例对象.尽管 User 这个单词总是代表人类,但一个Subject
可以是一个人,同时还可以代表一个第三方服务,守护账户,定时任务,或任何类似的可以与软件交互的东西.Subject
的每一个实例都(并且必须)绑定到一个SecurityManager
.当你使用Subject
的同时,那些交互将转换为与SecurityManager
的特定交互. -
SecurityManager:
SecurityManager
是Shiro架构的核心,表现为一个雨伞的伞轴聚合了内部安全组件为一个对象视图.然而,一个应用只有一个配置好的SecurityManager,它通常被留在一边,因为应用开发者大部分时间都花费在Subject
API上了.我们将在之后介绍SecurityManager的细节,但你必须要意识到当你使用
Subject
的时候,实际上是SecurityManager
在后面为每一个Subject
实现了具体的功能,这也反映在了上面的基本流程图中.
-
Realms: Realms 表现为一个桥或连接器,且位于Shiro和你的应用的安全数据之间.当需要与安全相关的数据比如用户账户信息交互的时候,比如登录或者认证,此时Shiro将会从应用程序的配置中找到一个或者多个Realms,再从Realms里面查找经过配置的安全数据(主要是用户账户相关信息).
在某种意义上说,Realm本质上就是特定的安全的DAO层:Realm也封装了数据源的连接细节,并根据需要将相关数据提供给Shiro.当配置Shiro的时候,你必须为用户的认证和授权设置至少一个Realm.可以为SecurityManager
配置多个或一个Realm,但不能没有.
Shiro提供了很多开箱即用的安全数据源的连接方式,比如LDAP,JDBC,基于文本的INI文件或properties文件,等等.如果默认的Realm无法满足你的需求,你还可以添加自定义的Realm实现来或许安全数据源.
和其他内部组件一致,Shiro的SecurityManager
管理着所有的Realm如何使用,这样可以使得所有的Subject
实例可以得到同样的安全并且一致的内部数据.
详细结构
下图展示了Shiro的核心结构概念,并对每个概念进行了简要的总结.
-
Subject (
org.apache.shiro.subject.Subject
)
一个对应于安全实体(比如当前用户,发来请求的第三方服务,正在执行的定时任务,等)的特定的视图,代表和当前软件交互的事物. -
SecurityManager (
org.apache.shiro.mgt.SecurityManager
)
正如前面提到的那样,SecurityManager
是Shiro架构的核心.它就像一把雨伞协调着所有的它可以管理的组件,以此来保证这些组件可以稳定的工作.它同样管理着所有Shiro应用的用户的视图,所以它知道如何对每个用户执行安全检查操作. -
Authenticator (
org.apache.shiro.authc.Authenticator
)
认证器是一个组件,主要负责当用户登录时进行身份认证的执行和反应,就是说当一个用户尝试去登录, 真正检查用户信息是否真实的流程是认证器来完成的.认证器知道如何协调一个或者多个储存相关用户或者账户信息的Realm.从Realm中获得的数据被用来确认用户身份,以此保证当前用户信息属实. -
Authentication Strategy(
org.apache.shiro.authc.pam.AuthenticationStrategy
)
假如不止一个Realm被配置了,认证策略将负责协调这些Realm在什么样的情况下身份验证尝试成功或者失败(比如,如果一个realm成功了,但其他失败了,这种情况下此用户被判定为登录成功还是失败?必须所有realm都成功还是只要一个成功就判定为登录成功呢?)
-
Authorizer (
org.apache.shiro.authz.Authorizer
)
授权者是一个组件,负责判定用户在应用中的访问权限.一个用户是否被允许做某件事的判定机制由授权者决定.与认证器类似,授权者同样知道如何协调访问多方后端数据源中的角色或者权限信息数据.授权者使用那些数据来判定一个用户是否拥有执行某段代码的权限. -
SessionManager (
org.apache.shiro.session.mgt.SessionManager
)
Session管理器知道如何创建和管理用户的Session生命周期,并以此来为用户提供一个健壮的Session使用环境.这是安全领域框架中的独特特性-Shiro拥有在任意环境下创建,管理,维护用户Session的能力,即使这里没有网络/Servlet或者EJB容器可用.默认情况下,只要条件允许,Shiro就会使用一个已存在的session管理机制,比如Servlet容器,但如果这里一个容器都没有, 比如一个标准独立应用或者没有网络的环境,它将使用其内置的企业会话管理来提供相同的编程体验.SessionDAO的存在允许任何数据源用于持久会话. -
SessionDAO (
org.apache.shiro.session.mgt.eis.SessionDAO
)
SessionDAO代表SessionManager执行Session的数据持久化操作(CRUD).这就运行任何数据存储插入会话管理层的结构内部.
-
CacheManager (
org.apache.shiro.cache.CacheManager
)
缓存管理器负责创建和管理缓存对象实例的声明周期,并被其他Shiro组件使用.因为Shiro可以获取很多终端数据源用于认证,授权和session管理,使用缓存可以提高使用这些数据源的性能.任何流行的开源或者商业缓存产品都可以被添加到shiro里面以提供更快更高效的用户体验. -
Cryptography (
org.apache.shiro.crypto.*
)
密码工具是企业安全框架必备的防御性武器.Shiro中crypto包下包含易于使用和理解的加密接口,哈希(又称为摘要)接口以及不同的编码解码实现. 此包下所有的类都被精心设计为非常简单易用且易于理解.使用Java自带的加密接口很困难,因此Shiro的加密接口简化了复杂的Java加密机制并使密码学易于为普通人使用. -
Realms (
org.apache.shiro.realm.Realm
)
如上所述,Realm表现为一座桥梁或者线路连接着shiro和你的应用中需要的安全数据.当需要与安全相关数据(如用户帐户原始数据)进行实际交互以执行身份验证(登录)和授权(访问控制)时,shiro将会从应用程序中配置的一个或多个Realm中查找这些内容.你可以根据需要配置尽可能多的Realm(通常每个数据源一个),Shiro将根据需要与这些Realm协调如何具体实现身份验证和授权.
The SecurityManager
由于Shiro的API鼓励以Subject
为中心的编程方法,因此大多数应用程序开发人员很少(如果有的话)直接与SecurityManager交互(但是框架开发人员有时可能会发现它很有用).即便如此,了解SecurityManager如何工作仍然很重要,特别是在为应用程序配置SecurityManager的时候.
设计
综上所述,应用程序的SecurityManager管理着应用程序中所有用户的安全执行操作和对应的安全状态.在Shiro的默认SecurityManager实现中,这包括:
- 认证
- 授权
- Session管理
- 缓存管理
- Realm协作
- 事件传播
- 记住我服务
- Subject创建
-
退出等
一个安全管理器组件管理着上述所有的功能,同时在一个实现类里面还要让这些东西灵活可定制是非常困难的一件事.
为了简化配置或者说灵活配置/可插入,Shiro的实现在设计上都是高度模块化的.事实上,模块化程度很高,以至于SecurityManager的实现(及其类层次结构)根本做不了什么事情.作为替代,SecurityManager实现主要充当轻量级的容器组件,将几乎所有行为委托给nested/wrapped的组件.这种"装饰器"设计模式反映在上面的详细架构图中.
当组件实际执行给定的代码逻辑时,SecurityManager知道如何实现和如何协调组件以表现出正确的行为.
SecurityManager的实现类和组件类也与JavaBeans兼容,它允许你(或配置机制)通过标准的JavaBeans访问/设置方法(get/set)轻松地定制可插入组件.这意味着Shiro的模块化体系结构可以非常容易的转换为自由配置.