shiro实战系列(七)之Realm

Realm 是一个能够访问应用程序特定的安全数据(如用户、角色及权限)的组件。Realm 将应用程序特定的数据转 换成一种 Shiro 能够理解的格式,这样 Shiro 能够提供一个单一的易理解的 Subject 编程 API,无论有多少数据源存在 或你应用程序特定的数据是怎样的。   Realm 通常和数据源是一对一的对应关系,如关系数据库,LDAP 目录,文件系统,或其他类似资源。因此,Realm 接口的实现使用数据源特定的 API 来展示授权数据(角色,权限等),如 JDBC,文件 IO,Hibernate 或 JPA,或其他 数据访问 API。

 

Realm 实质上就是一个特定安全的 DAO   因为这些数据源大多通常存储身份验证数据(如密码的凭证)以及授权数据(如角色或权限),每个 Shiro Realm 能够执行身份验证和授权操作。

 

(1)Realm configuration

如果使用 Shiro 的 INI 配置文件,你能够自定义及引用 Realm,就像在[main]项中的任何其他对象一样,但它们在 securityManager 中采用两种方法之一进行配置:显式或隐式。

 

(2)Explicit Assignment

基于迄今的 INI 配置知识,这是一个显示的配置方法。在定义一个或多个 Realm 后,你将它们作为 securityManager 对象的集合属性

 

 

 

显式分配是确定的——你控制具体使用哪一个 Realm 及它们用于身份验证和授权的顺序。Realm 顺序的作用在 Authentication 章的 Authentication Sequence 节进行了详细的介绍。

 

(3) Implicit Assignment

Not Preferred(不推荐)         

这种方法可能引发意想不到的行为,如果你改变 realm 定义的顺序的话。建议你避免使用此方法,并使用显式分配, 它拥有确定的行为。该功能很可能在未来的 Shiro 版本中被废弃或移除。   如果出于某些原因你不想显式地配置 securityManager.realms 的属性,你可以允许 Shiro 检测所有配置好的 realm 并直接将它们指派给 securityManager。   使用这种方法,realm 将会按照它们预先定义好的顺序来指派给 securityManager 实例。   也就是说,对于下面的 shiro.ini 示例:

blahRealm = com.company.blah.Realm              

fooRealm =  com.company.foo.Realm

 

barRealm = com.company.another.Realm

 # no securityManager.realms assignment here 

基本上和下面这一行具有相同的效果:

 securityManager.realms = $blahRealm, $fooRealm, $barRealm

 

然而,实现隐式分配,只是 realm 定义的顺序直接影响到了它们在身份验证和授权尝试中的访问顺序。如果你改变 它们定义的顺序,你将改变主要的 Authenticaor 的 Authentication Sequence 是如何起作用的。   由于这个原因,以及保证明确的行为,我们推荐使用显式分配而不是隐式分配。 

 

(3) Realm Authentication

当你理解了 Shiro 的主要 Authentication 工作流后,了解在一个授权尝试中当 Authenticator 与 Realm 交互时到底发 生了什么是很重要的。

a.Supporting AuthenticationTokens

在 authentication sequence 中已经提到,当在 Realm 被访问来执行一个授权尝试之前,它的supports 方法被调用。 如果返回值为 true,则只有这样它的 getAuthenticationInfo(token)方法才会被调用。   通常 realm 会检查提交的 token 的类型(接口或类)来判断它是否能够处理它。

例如,一个能够处理生物数据的 realm 可能就一点也不理解 UsernamePasswordTokens,这样它将从 supports 方法返回 false。  

 

b.Handling supported AuthenticationTokens

若 Realm 支持一个提交的 AuthenticationToken,那么 Authenticator 将会调用该 Realm 的 getAuthenticationInfo(token) 方法。这有效地代表了一个与 Realm 的后备数据源的授权尝试。该方法按以下方法进行: 

 1. 为主要的识别信息(帐户识别信息)检查 token。

 2. 基于 principal 在数据源中寻找相吻合的帐户数据。

3. 确保 token 支持的 credentials 匹配那些存储在数据源的。

4. 若 credentials 匹配,返回一个封装了 Shiro 能够理解的帐户数据格式的 AuthenticationInfo 实例。

5. 若 credentials 不匹配,则抛出 AuthenticationException 异常。  

这是对所有 Realm getAuthenticationInfo 实现的最高级别的工作流。在此方法中,Realm 可以自由地做任何它们想做 的,如记录在审计日志的尝试,更新数据记录,或任何其他可以对该数据存储的身份验证尝试有意义的东西。   唯一需要的东西就是,如果 credentials 匹配给予的 principal(s),那么返回一个非空的 AuthenticationInfo 实例来代表 来自于该数据源的 Subject 帐户信息。 

 

c.Save Time               

直接实现 Realm 接口可能导致时间消耗及错误。大多数人们选择 AuthorizingRealm 抽象类的子类而不是从头开始。 这个类实现了常用的 authentication 及 authorization 工作流来节省你的时间和精力。   

 

d.Credentials Matching

 

在上面的 realmauthentication 工作流中,Realm 不得不验证 Subject 提交的 credentials(如,,密码)必须匹配存储 在数据存储中的 credentials。如果匹配,则被认为身份验证成功,同时系统还必须验证终端用户的身份。   Realm Credentials Matching                        这是每个 Realm 的责任,去匹配提交的 credentials 和那些存储在 Realm 后备数据存储中的 credentials,而不是 Authenticator 的责任。每个 Realm 拥有有关私人信息的 credentials 格式,存储及能够执行详细的 credentials 匹配, 然而 Authenticator 只是一个普通的工作量组件。

 

credentials 的匹配过程在所有应用程序中几乎一样,通常不一样的是进行比较的数据。为了确保该过程是可插入及 可定制的如果需要的话,AuthenticatingRealm 及它的子类支持 CredentialsMatcher 来执行 credentials 对比的概念。   在发现帐户数据后,它以及提交的 AuthenticationToken 用来代表一个 CredentialsMatcher 来判断所提交的是否匹配 所存储的。   Shiro 拥有某些可以让你立即使用的 CredentialsMatcher 实现,如 SimpleCredenticalsMatcher 和 HashedCredentialsMatcher,但如果你想为自定义的逻辑配置一个自定义的实现,你可以像下面一样直接做:

 

 e.Simple Equality Check

所有 Shiro 立即可用的 Realm 的实现默认使用 SimpleCredentialsMatcher。SimpleCredentialsMatcher 执行一个普通的 直接平等检查,关于存储的帐户 credentials 与在 AuthenticationToken 所提交的之间的检查。 

例如,若一个 UsernamePasswordToken 被提交后,则 SimpleCredentialsMatcher 验证该密码实际上是否与存储在数 据库中的密码相同。   SimpleCredentialsMatcher 不仅仅为字符串执行直接相等比较。它能够处理大多数常用的字节码,像字符串,字符数 组,字节数组,文件及输入流。请参考它的 JavaDoc 获取更多。

 

f.Hashing Credentials

并非是存储credentials 在其原始的 form 及执行原始/普通的比较,一个更安全的方式存储终端用户的credentials(如, 密码)是在存储它们到数据存储之前将它们单向散列化。   这确保终端用户的 credentials 绝不会以原始的 form 存储,而且没人会知道原始值。这是一个比纯文本或原始比较 更为安全的机制,同时所有关注安全的应用程序应该较非哈希化的存储更为喜欢。   为了支持这些首选的加密哈希策略,Shiro 提供了 HashedCredentialsMatcher 的实现配置在 realm 上而不是上述 SimpleCredentialsMatcher。   哈希 credentials 及 salting 和多个哈希迭代的好处超出了该 Realm 文档的范围,但绝对要阅读 HashedCredentialsMatcher 的 JavaDoc,其中将详细介绍这些细节。  

g.Hashing and Corresponding Matchers

那么,你如何很容易地配置一个启用 Shiro 的应用程序呢?   Shiro 提供了多个 HashedCredentialsMatcher 子类实现。你必须在你的 realm 中配置指定的实现来匹配你 hash 化你用 户 credentials 时使用的哈希算法。  

例如,假设你的应用程序为身份验证使用用户名/密码对。由于上文所述的哈希凭据的好处,假设当你创建一个用 户帐户时,你想使用 SHA-256 算法单向散列用户的密码。你将哈希用户输入的纯文本密码并保持该值:

 

 

既然你使用 SHA-256 散列你用户的密码,你需要告诉 Shiro 使用合适的 HashedCredentialsMatcher 以匹配你的哈希参 数选择。在这个例子中,我们创建了一个随机的 salt 并执行 1024 次哈希迭代,为了强大的安全性(请参见 HashedCredentialsMatcher 的 JavaDoc 获取原因)。这里是完成这项工作的 Shiro INI 配置: 

h.SaltedAuthenticationInfo

为了确保这一工程,最后要做的事情是,你的 Realm 实现必须返回一个 SaltedAuthenticationInfo 实例而不是一个普 通的 AuthenticationInfo 实例。SaltedAuthenticationInfo 接口确保在你创建用户帐户(如,user.setPasswordSalt(Salt); call above)时使用的 salt 能够被 HashedCredentialsMatcher 引用.   HashedCredentialsMatcher 需要该 salt 为了能够在提交的 AuthenticationToken 上执行相同的哈希技术来判断该 token 是否匹配你保存在数据存储中的东西。因此,如果你为用户密码使用 salting(而且你应该这样做!!!),确保你 的 Realm 实现能够通过返回的 SaltedAuthenticationInfo 实例代表密码。  

 

i.Disabling Authentication

如果出于某些原因,你不想用 Realm 对数据源执行身份验证(也许是由于你只想 Realm 执行授权),你可以彻底地 禁用 Realm 对身份验证的支持通过从 Realm 的 support 方法返回 false。然后你的 realm 在身份验证尝试中永远不会 被访问到。   当然,至少需要一个能够支持 AuthenticationTokens 且已配置的 Realm,如果你想验证 Subjects

 

posted @ 2018-06-11 20:37  挑战者V  阅读(571)  评论(0编辑  收藏  举报