Apache Shiro的架构
Apache Shiro的设计目标是通过其直观易用来简化安全应用的开发, Shiro的核心设计以多数人对应用安全的认识为模型——在某个环境下某人或某物与应用的交互。
应用软件通常是基于用户故事(User Story)而设计,就是说我们经常会根据用户如何与系统交互来设计界面或服务接口。举个例子,“如果用户登录了系统,你会向其显示一个按钮,单击后可以看到他们的帐号信息。如果没有登录,则会显示一个登录按钮。”
这个例子说明应用主要是来满足用户的需求与需要,即使这个“用户”是其他的软件系统而不是人类,你还是要基于谁(或什么)在跟你的软件交互来写代码以反映此行为。
Shiro在设计上体现了上面的概念,既满足了对软件开发人员的直观性,又保留了在实践中的易用性。
概览
Shiro的架构主要有三个顶级概念:Subject, SecurityManager和Realms。图中展示了这些组件间的交互,接下来详细介绍:
- Subject: 正如我们在教程里提到的, Subject实际上是安全领域里的“当前执行用户”。“User”通常意指人类,而Subject 却可以是一个人,或者是第三方服务,守护进程帐户,定时任务等等——可以是基本上任何正与软件交互的事物。
Subject的实例都会(也是必须)绑定一个SecurityManager,对Subject的操作会转为Subject与SecurityManager之间的交互。 - SecurityManager: SecurityManager是Shiro架构的核心,像个“保护伞”一样协调内部组件运作。不过一旦SecurityManager配置完成,它就被放一边去了,通常开发人员只需要使用Subject。
稍后详细介绍SecurityManager,现在你需要知道的是,当使用Subject进行各种操作时,在背后做苦力活的可是SecurityManager,这一点在上面的图中有所体现。 - Realms: Realms在Shiro和你的安全数据之间扮演“桥梁”或“连接器”的角色。当需要用到安全数据比如用户帐号来进行认证或授权时,Shiro会从应用配置的一个或多个Realms中来查找。
这个意义上Realm其实就是安全(操作)特有的DAO:它封装了数据源的细节,并在Shiro需要时提供相关数据。配置Shiro时,必须至少得有一个Realm以用于认证(和/或)授权。在SecurityManager中可以配置Realm,至少得有一个。
Shiro自带一些开箱即用的Realms用于连接像是LDAP,关系数据库(JDBC),文本配置源(比如INI)以及属性文件等等这样的安全数据来源。如果默认的不能满足你的需要,你也可以自己实现一个Realm。
同其他内部组件一样,Realms由SecurityManager管理着如何获取数据并传给Subject的实例。
详细架构
下面是Shiro的核心架构以及一些简短的介绍:
- Subject (org.apache.shiro.subject.Subject)
安全视角下与软件交互的实体(用户,第三方服务等等)。
- SecurityManager (org.apache.shiro.mgt.SecurityManager)
如上,SecurityManager 是Shiro架构的核心,起协调内部各组件的作用。同样也管理Shiro的所有用户,可以进行用户相关的安全操作。
- Authenticator (org.apache.shiro.authc.Authenticator)
Authenticator是用于执行用户的认证行为的组件,当用户登录时,登录的逻辑操作由Authenticator执行。Authenticator知道如何与存储用户/帐号信息的一个或多个Realms协作。从这些Realms获取的数据用于验证用户的真实身份。
- Authentication Strategy (org.apache.shiro.authc.pam.AuthenticationStrategy)
如果配置了不止一个Realm,AuthenticationStrategy将用来决定什么样的条件下认证会成功或失败(比如,如果一个realm的数据认证成功而其他的却失败了,能算是认证通过吗?还是必须所有的都认证成功?还是只要一个就可以?)。
- Authentication Strategy (org.apache.shiro.authc.pam.AuthenticationStrategy)
- Authorizer (org.apache.shiro.authz.Authorizer)
Authorizer用于控制程序中用户的访问,最终决定用户是否被允许做什么。和Authenticator一样, Authorizer也会与底层的数据源交互来获取角色与权限信息,从而准确控制用户的行为。
- SessionManager (org.apache.shiro.session.mgt.SessionManager)
SessionManager可以创建用户会话(Session)维护其生命周期并在所有环境中提供了强大的用户体验。Shiro可以管理任何环境下的用户会话,即使是非Web/Servlet或EJB容器下——这是区别于其他安全框架而特有的功能。默认情况下,Shiro会使用(容器)提供的会话管理机制,但如果没有,比如在独立的程序或非Web环境中,Shiro将使用它内置的企业会话管理功能以提供一致的编程体验。SessionDAO可使用各种数据源来存储会话。
- SessionDAO (org.apache.shiro.session.mgt.eis.SessionDAO)
SessionDAO替SessionManager执行会话的存储操作(CRUD)。任何的数据存储都可以接入到会话管理中。
- SessionDAO (org.apache.shiro.session.mgt.eis.SessionDAO)
- CacheManager (org.apache.shiro.cache.CacheManager)
CacheManager创建与管理缓存的实例与生命周期以供Shiro中的其他组件使用。因为在Shiro中可以使用多个数据源用于认证、授权和会话管理,而缓存可以在使用这些数据源时改善性能,所以也就成为框架中很重要的一个功能。 Shiro中可以使用任何开源或商业的缓存方案。
- Cryptography (org.apache.shiro.crypto.*)
加密是企业级安全框架固有的功能,Shiro的crypto包对密码、哈希和各种编码实现提供了易于使用和理解的封装 。该包下的所有类都为此精心设计,那些用过Java的原生安全加密功能的人应该了解这是多么大的一个挑战。Shiro的加密API简化了Java加密中的复杂性。
- Realms (org.apache.shiro.realm.Realm)
如上所知,Realm在shiro与应用安全数据之间扮演“桥梁”/“连接器”的角色。每当因为执行认证或授权操作而需要像用户帐号等安全相关的数据时,shiro会从程序配置的一个或多个Realm中查询。你可以根据需要配置多个Realm。
SecurityManager
因为Shiro的API鼓励开发人员使用以Subject为中心的编程方式,所以大多数情况下不会用到SecurityManager,即使如此,知道如何配置以及SecurityManager的运行机制还是很重要的。
设计
如之前所说,SecurityManager 执行安全(相关)操作并管理应用中所有用户的状态,默认的SecurityManager实现中包含:
- 认证(Authentication)
- 授权(Authorization)
- 会话管理(Session Management)
- 缓存管理(Cache Management)
- Realm管理
- 事件传播(Event propagation)
- 自动登陆服务("Remember Me" )
- 创建账号
- 登陆登出
等等但将如此多功能包含在一个组件中,同时又要做到灵活可定制还是挺困难的。
为了简化配置实现灵活的可插拔性,Shiro的实现在设计上高度模块化——甚至SecurityManager(及其继承类)基本上都不用做太多事了,相反, SecurityManager更多的是作为一个轻量“容器(container)”, 将功能的实现委托给内部组件。这种“封装器”的设计在上面的架构图中已有体现。
当组件执行逻辑的时候,SecurityManager知道如何以及何时去协调组件做出正确的行为。
SecurityManager和 JavaBean 兼容,这允许你(或者配置途径)通过标准的JavaBean 访问/设置方法(get/set)很容易地定制插件。Shiro 模块可以根据用户行为转化成简易的配置。
简易的配置
因为兼容JavaBean,SecurityManager很容易在任何支持Javabean配置的框架或容器中添加组件,如Spring、Guice、JBoss等等。接下来讨论 配置。