Apache Shiro 领域

Realm是可以访问特定于应用程序的安全性数据(例如用户,角色和权限)的组件。 Realm将此特定于应用程序的数据转换为 Shiro 可以理解的格式,因此 Shiro 可以反过来提供单个易于理解的Subject编程 API,无论存在多少数据源或您的数据有多少特定于应用程序。

领域通常与数据源(例如关系数据库,LDAP 目录,文件系统或其他类似资源)具有一对一的关联。这样,Realm接口的实现使用特定于数据源的 API 来发现授权数据(角色,权限等),例如 JDBC,文件 IO,Hibernate 或 JPA 或任何其他数据访问 API。

Tip

领域本质上是特定于安全性的DAO

因为这些数据源中的大多数通常都存储身份验证数据(密码等凭据)以及授权数据(例如角色或权限),所以每个 Shiro Realm都可以同时执行身份验证和授权操作。

Realm Configuration

如果使用 Shiro 的 INI 配置,则可以像[main]部分中的任何其他对象一样定义和引用Realms,但是可以通过以下两种方式之一在securityManager上配置它们:显式或隐式。

Explicit Assignment

根据到目前为止的 INI 配置知识,这是一种显而易见的配置方法。定义一个或多个领域之后,可以将它们设置为securityManager对象上的集合属性。

For example:

fooRealm = com.company.foo.Realm
barRealm = com.company.another.Realm
bazRealm = com.company.baz.Realm

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

显式分配是确定性的-您可以精确控制要使用的领域以及用于身份验证和授权的“Sequences”。领域 Sequences 的影响在“身份验证”一章的“ Authentication Sequence”部分中进行了详细说明。

Implicit Assignment

Not Preferred

如果您更改领域的定义 Sequences,则隐式分配可能会导致意外行为。建议您避免使用这种方法,而应使用具有确定性行为的显式分配。隐式分配很可能在以后的 Shiro 版本中被弃用/删除。

如果由于某种原因您不想显式配置securityManager.realms属性,则可以允许 Shiro 检测所有已配置的领域并将它们直接分配给securityManager

使用这种方法,将域按照“它们定义的 Sequences”分配给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

但是,请注意,使用隐式分配,仅定义领域的 Sequences 会直接影响在身份验证和授权尝试期间如何查询它们。如果更改它们的定义 Sequences,则将更改主AuthenticatorAuthentication Sequence的功能。

因此,为了确保确定性行为,我们建议使用“显式分配”而不是“隐式分配”。

Realm Authentication

理解 Shiro 的母版Authentication workflow之后,很重要的一点是,准确了解在身份验证尝试期间AuthenticatorRealm交互时会发生什么。

Supporting AuthenticationTokens

authentication sequence中所述,在咨询Realm进行身份验证尝试之前,将调用其supports方法。如果返回值为true,则仅将调用其getAuthenticationInfo(token)方法。

通常,领域会检查所提交令牌的类型(接口或类),以查看其是否可以处理它。例如,处理 bioFeature 数据的领域可能根本无法理解UsernamePasswordTokens,在这种情况下,它将从supports方法返回false

处理支持的 AuthenticationToken

如果Realm supports提交了AuthenticationToken,则Authenticator将调用 Realm 的getAuthenticationInfo(token)方法。这有效地表示使用Realm's支持数据源进行的身份验证尝试。方法,依次为:

  • 检查token的身份验证主体(帐户身份验证信息)

  • 基于principal,在数据源中查找相应的帐户数据

  • 确保提供的令牌credentials与数据存储中存储的令牌匹配

  • 如果凭据匹配,则返回AuthenticationInfo实例,该实例以 Shiro 理解的格式封装帐户数据

  • 如果凭据不匹配,则抛出AuthenticationException

这是所有 Realm getAuthenticationInfo实施的最高级别的工作流程。领域可以自由地在此方法期间做任何想做的事情,例如将尝试记录在审核日志中,更新数据记录或其他对该数据存储进行身份验证尝试有意义的事情。

唯一需要做的就是,如果凭据与给定的主体匹配,则返回一个非空的AuthenticationInfo实例,该实例表示该数据源中的使用者帐户信息。

Save Time

直接实现Realm接口可能很耗时且容易出错。大多数人选择继承AuthorizingRealm抽象类,而不是从头开始。此类实现通用的身份验证和授权工作流,以节省您的时间和精力。

Credentials Matching

在上述领域认证工作流程中,领域必须验证Subject的提交凭据(例如密码)必须与数据存储中存储的凭据匹配。如果它们匹配,则认为身份验证成功,并且系统已经验证了最终用户的身份。

Realm Credentials Matching

将提交的凭证与存储在其支持数据存储区中的凭证进行匹配是每个 Realm 的责任,而不是“认证者”的责任。每个“领域”都对凭证格式和存储有深入的了解,并且可以执行详细的凭证匹配,而“身份验证器”是通用的工作流程组件。

凭证匹配过程在所有应用程序中几乎都是相同的,并且通常仅根据所比较的数据而有所不同。为确保此过程在必要时可插入和可自定义,AuthenticatingRealm及其子类支持CredentialsMatcher的概念以执行凭据比较。

发现帐户数据后,将其和提交的AuthenticationToken呈现给CredentialsMatcher,以查看提交的内容是否与数据存储中存储的内容匹配。

Shiro 提供了CredentialsMatcher实现,例如SimpleCredentialsMatcherHashedCredentialsMatcher实现,但是如果您想为自定义匹配逻辑配置自定义实现,则可以直接这样做:

Realm myRealm = new com.company.shiro.realm.MyRealm();
CredentialsMatcher customMatcher = new com.company.shiro.realm.CustomCredentialsMatcher();
myRealm.setCredentialsMatcher(customMatcher);

或者,如果使用 Shiro 的 INI configuration

[main]
...
customMatcher = com.company.shiro.realm.CustomCredentialsMatcher
myRealm = com.company.shiro.realm.MyRealm
myRealm.credentialsMatcher = $customMatcher
...

简单平等检查

Shiro 的所有现成的Realm实现默认都使用SimpleCredentialsMatcherSimpleCredentialsMatcher对存储的帐户凭据与AuthenticationToken中提交的内容进行普通的直接相等性检查。

例如,如果提交了UsernamePasswordToken,则SimpleCredentialsMatcher验证提交的密码与数据库中存储的密码完全相同。

SimpleCredentialsMatcher不仅可以对字符串执行直接相等比较,还可以对字符串进行比较。它可以与大多数常见的字节源一起使用,例如字符串,字符数组,字节数组,文件和 InputStreams。有关更多信息,请参见其 JavaDoc。

Hashing Credentials

与其以原始格式存储凭据并执行原始/普通比较,不如存储最终用户凭据(例如密码)安全得多,而是先将它们单次散列,然后再将其存储在数据存储中。

这样可以确保最终用户的凭据永远不会以原始格式存储,并且没有人知道原始/原始值。与纯文本或原始比较相比,这是一种更加安全的机制,并且所有注重安全性的应用程序都应优先于非哈希存储使用此方法。

为了支持这些首选的密码哈希策略,Shiro 提供了HashedCredentialsMatcher个实现,这些实现将在领域中配置,而不是前面提到的SimpleCredentialsMatcher

散列凭证以及加盐和多次散列迭代的好处不在本Realm文档的范围之内,但是一定要阅读HashedCredentialsMatcher JavaDoc,它详细介绍了这些原理。

哈希和对应的匹配器

那么,如何配置启用 Shiro 的应用程序来轻松做到这一点?

Shiro 提供了多个HashedCredentialsMatcher子类实现。您必须在域上配置特定的实现,以匹配用于哈希用户凭据的哈希算法。

例如,假设您的应用程序使用用户名/密码对进行身份验证。并且由于上述散列凭据的好处,因此,假设您要在创建用户帐户时使用SHA-256算法对用户密码进行单向散列。您将对用户 Importing 的纯文本密码进行哈希处理并保存该值:

import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
...

//We'll use a Random Number Generator to generate salts.  This 
//is much more secure than using a username as a salt or not 
//having a salt at all.  Shiro makes this easy. 
//
//Note that a normal app would reference an attribute rather 
//than create a new RNG every time: 
RandomNumberGenerator rng = new SecureRandomNumberGenerator();
Object salt = rng.nextBytes();

//Now hash the plain-text password with the random salt and multiple 
//iterations and then Base64-encode the value (requires less space than Hex): 
String hashedPasswordBase64 = new Sha256Hash(plainTextPassword, salt, 1024).toBase64();

User user = new User(username, hashedPasswordBase64);
//save the salt with the new account.  The HashedCredentialsMatcher 
//will need it later when handling login attempts: 
user.setPasswordSalt(salt);
userDAO.create(user);

由于您是SHA-256对用户密码进行哈希处理,因此您需要告诉 Shiro 使用适当的HashedCredentialsMatcher来匹配您的哈希处理首选项。在此示例中,我们创建了一个随机盐,并执行了 1024 次哈希迭代以增强安全性(有关原因,请参见HashedCredentialsMatcher JavaDoc)。这是 Shiro INI 的配置,可以完成这项工作:

[main]
...
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
# base64 encoding, not hex in this example:
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
# This next property is only needed in Shiro 1.0\.  Remove it in 1.1 and later:
credentialsMatcher.hashSalted = true

...
myRealm = com.company.....
myRealm.credentialsMatcher = $credentialsMatcher
...

SaltedAuthenticationInfo

确保这项工作有效的最后一件事是您的Realm实现必须返回一个SaltedAuthenticationInfo实例,而不是普通的AuthenticationInfo实例。 SaltedAuthenticationInfo接口可确保HashedCredentialsMatcher引用创建用户帐户时使用的盐(例如,上面的user.setPasswordSalt(salt);调用)。

HashedCredentialsMatcher需要加盐才能对提交的AuthenticationToken执行相同的哈希技术,以查看令牌是否与您在数据存储区中保存的令牌匹配。因此,如果您对用户密码使用盐化(应该这样做!!!),请确保Realm实现通过返回SaltedAuthenticationInfo实例来表示。

Disabling Authentication

如果由于某种原因,您不希望 Realm 对数据源执行身份验证(也许是因为您只希望 Realm 执行授权),则可以通过始终从 Realm 的supports方法返回false来完全禁用 Realm 对身份验证的支持。这样一来,在进行身份验证时就永远不会查询您的领域。

当然,如果要验证主题,则至少需要配置一个Realm才能支持 AuthenticationToken。

Realm Authorization

SecurityManagerPermissionRole检查任务委托给Authorizer,默认为ModularRealmAuthorizer

基于角色的授权

在主题上调用重载方法 hasRoles 或 checkRoles 方法之一时

  • Subject代表SecurityManager以确定是否分配了给定角色

  • SecurityManager然后委派给Authorizer

  • Authorizer然后逐个引荐所有授权领域,直到找到分配给该主题的指定角色。如果没有任何领域授予主题,则返回 false 来拒绝访问

  • 授权领域AuthorizationInfo getRoles()方法以获取分配给主题的所有角色

  • 如果在 AuthorizationInfo.getRoles 调用返回的角色列表中找到给定的 Role,则授予访问权限。

基于权限的授权

在主题上调用重载方法isPermitted()checkPermission()方法之一时:

  • Subject委派任务向 SecurityManager 授予或拒绝权限

  • SecurityManager然后委托给授权者

  • 然后,授权者逐一引用所有授权者领域,直到授予权限为止
    如果未由任何授权领域授予许可,则拒绝主题

  • 授权领域执行以下操作以检查是否允许主题:

一个。首先,它通过在AuthorizationInfo上调用 getObjectPermissions()和 getStringPermissions 方法并汇总结果来直接识别分配给 Subject 的所有权限。

b。如果注册了RolePermissionResolver,则通过调用RolePermissionResolver.resolvePermissionsInRole()来根据分配给主题的所有角色检索权限。

C。对于来自 a 的汇总权限。和 b。调用 implies()方法以检查这些权限中的任何一个是否隐含已检查的权限。见WildcardPermission

协助处理文档

尽管我们希望该文档对您使用 Apache Shiro 所做的工作有所帮助,但社区一直在不断改进和扩展文档。如果您想为 Shiro 项目提供帮助,请考虑在需要的地方更正,扩展或添加文档。您提供的每一点帮助都会扩大社区,进而改善 Shiro。

提交文档的最简单方法是通过单击下面的Edit链接提交请求请求,然后将其发送到User Forum用户邮件列表