Apache Shiro 配置

Shiro 旨在在任何环境中工作,从简单的命令行应用程序到最大的企业集群应用程序。由于环境的多样性,因此存在许多适用于配置的配置机制。本节仅介绍 Shiro 核心支持的配置机制。

Many Configuration Options

Shiro 的SecurityManager实现和所有支持的组件都与 JavaBeans 兼容。这使得 Shiro 几乎可以使用任何配置格式进行配置,例如常规 Java,XML(Spring,JBoss,Guice 等),YAML,JSON,Groovy Builder 标记等。

Programmatic Configuration

创建 SecurityManager 并将其提供给应用程序的最简单的绝对方法是创建org.apache.shiro.mgt.DefaultSecurityManager并将其连接到代码中。例如:

Realm realm = //instantiate or acquire a Realm instance.  We'll discuss Realms later.
SecurityManager securityManager = new DefaultSecurityManager(realm);

//Make the SecurityManager instance available to the entire application via static memory: 
SecurityUtils.setSecurityManager(securityManager);

令人惊讶的是,仅需三行代码,您便拥有了一个适用于许多应用程序的功能齐全的 Shiro 环境。那有多容易!!

SecurityManager 对象图

Architecture章所述,Shiro 的SecurityManager实现本质上是嵌套的特定于安全性组件的模块化对象图。由于它们也与 JavaBeans 兼容,因此可以调用任何嵌套组件gettersetter方法来配置SecurityManager及其内部对象图。

例如,如果要配置SecurityManager实例以使用自定义SessionDAO自定义Session Management,则可以使用嵌套的 SessionManager 的setSessionDAO方法直接设置SessionDAO

...

DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);

SessionDAO sessionDAO = new CustomSessionDAO();

((DefaultSessionManager)securityManager.getSessionManager()).setSessionDAO(sessionDAO);
...

使用直接方法调用,您可以配置SecurityManager的对象图的任何部分。

但是,就象程序定制一样简单,它并不代表大多数实际应用程序的理想配置。程序配置可能不适合您的应用的原因有几个:

  • 它要求您了解并实例化直接实现。如果您不必了解具体的实现以及在哪里找到它们,那就更好了。

  • 由于 Java 具有类型安全性,因此需要将通过get*方法获得的对象强制转换为它们的特定实现。太多的转换是丑陋的,冗长的,并且将您紧密耦合到实现类。

  • SecurityUtils.setSecurityManager方法调用使实例化的SecurityManager实例成为 VM 静态单例,这对于许多应用程序而言虽然很好,但如果在同一个 JVM 上运行多个启用 Shiro 的应用程序,则会导致问题。如果实例是应用程序单例,而不是静态内存引用,则可能会更好。

  • 它要求您每次要更改 Shiro 配置时都重新编译应用程序。

但是,即使有这些警告,直接编程操作方法在像智能手机应用程序这样的内存受限环境中仍然可能有价值。如果您的应用程序不在内存受限的环境中运行,您会发现基于文本的配置更易于使用和阅读。

INI Configuration

相反,大多数应用程序都受益于基于文本的配置,该配置可以独立于源代码进行修改,甚至使那些不熟悉 Shiro API 的人也更容易理解。

为了确保基于共同点的基于文本的配置机制可以在所有环境中以最少的第三方依赖性运行,Shiro 支持INI format来构建SecurityManager对象图及其支持组件。 INI 易于阅读,易于配置,易于设置,非常适合大多数应用。

通过 INI 创建 SecurityManager

这是两个如何基于 INI 配置构建 SecurityManager 的示例。

来自 INI 资源的 SecurityManager

我们可以从 INI 资源路径创建 SecurityManager 实例。分别以file:classpath:url:作为前缀,可以从文件系统,Classpath 或 URL 获取资源。本示例使用Factory从 Classpath 的根中提取shiro.ini文件并返回SecurityManager实例:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.config.IniSecurityManagerFactory;

...

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

来自 INI 实例的 SecurityManager

如果需要,可以通过org.apache.shiro.config.Ini类以编程方式构造 INI 配置。 Ini 类的功能类似于 JDK java.util.Properties类,但是还支持按节名称进行分段。

For example:

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.util.Factory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.IniSecurityManagerFactory;

...

Ini ini = new Ini();
//populate the Ini instance as necessary
...
Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);

现在,我们知道了如何从 INI 配置中构造SecurityManager,下面让我们看一下如何定义 Shiro INI 配置。

INI Sections

INI 基本上是一种文本配置,由按唯一命名的部分组织的键/值对组成。键仅在每个部分中是唯一的,而不是在整个配置中都是唯一的(不同于 JDK Properties)。但是,每个部分都可以看作是单个Properties定义。

带 Comments 的行可以以 Octothorpe(#-又名“哈希”,“英镑”或“数字”符号)或分号(';')开头。

这是 Shiro 理解的部分示例:

# =======================
# Shiro INI configuration
# =======================

[main]
# Objects and their properties are defined here,
# Such as the securityManager, Realms and anything
# else needed to build the SecurityManager

[users]
# The 'users' section is for simple deployments
# when you only need a small number of statically-defined
# set of User accounts.

[roles]
# The 'roles' section is for simple deployments
# when you only need a small number of statically-defined
# roles.

[urls]
# The 'urls' section is used for url-based security
# in web applications.  We'll discuss this section in the
# Web documentation

[main]

[main] 部分是配置应用程序的SecurityManager实例及其任何依赖项(例如Realm)的地方。

配置对象实例(例如 SecurityManager 或它的任何依赖项)听起来很难使用 INI,因为在 INI 中,我们只能使用名称/值对。但是通过一些约定和对对象图的理解,您会发现您可以做很多事情。 Shiro 使用这些假设来启用简单但相当简洁的配置机制。

我们经常喜欢将这种方法称为“穷人”依赖注入,尽管它不如成熟的 Spring/Guice/JBoss XML 文件那么强大,但是您会发现它可以完成很多工作而没有太多复杂性。当然,这些其他配置机制也可用,但是使用 Shiro 并不是必需的。

只是为了激发您的胃口,这里有一个有效的[main]配置示例。我们将在下面详细介绍它,但是您可能会发现仅凭直觉就已经了解了很多事情:

[main]
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher

myRealm = com.company.security.shiro.DatabaseRealm
myRealm.connectionTimeout = 30000
myRealm.username = jsmith
myRealm.password = secret
myRealm.credentialsMatcher = $sha256Matcher

securityManager.sessionManager.globalSessionTimeout = 1800000

定义对象

请考虑以下[main]部分代码段:

[main]
myRealm = com.company.shiro.realm.MyRealm
...

此行实例化类型为com.company.shiro.realm.MyRealm的新对象实例,并使该对象在 myRealm 名称下可用,以供进一步参考和配置。

如果实例化的对象实现了org.apache.shiro.util.Nameable接口,则将使用名称值(在本示例中为 myRealm )在对象上调用Nameable.setName方法。

设置对象属性

Primitive Values

只需使用等号即可分配简单的原始属性:

...
myRealm.connectionTimeout = 30000
myRealm.username = jsmith
...

这些配置行转换为方法调用:

...
myRealm.setConnectionTimeout(30000);
myRealm.setUsername("jsmith");
...

这怎么可能?假定所有对象都是Java Beans兼容的POJO s。

在幕后,默认情况下,Shiro 在设置这些属性时使用 Apache Commons BeanUtils完成所有繁重的工作。因此,尽管 INI 值是文本,但是 BeanUtils 知道如何将字符串值转换为适当的原始类型,然后调用相应的 JavaBeans setter 方法。

Reference Values

如果您需要设置的值不是原始类型,而是另一个对象,该怎么办?好了,您可以使用美元符号($)来引用先前定义的实例。例如:

...
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
...
myRealm.credentialsMatcher = $sha256Matcher
...

这只是找到由名称 sha256Matcher 定义的对象,然后使用 BeanUtils 在 myRealm 实例上设置该对象(通过调用myRealm.setCredentialsMatcher(sha256Matcher)方法)。

Nested Properties

使用 INI 线的等号左侧的虚线表示法,可以遍历对象图以到达要设置的最终对象/属性。例如,此配置行:

...
securityManager.sessionManager.globalSessionTimeout = 1800000
...

(通过 BeanUtils)转换为以下逻辑:

securityManager.getSessionManager().setGlobalSessionTimeout(1800000);

可以遍历图的深度:object.property1.property2....propertyN.value = blah

BeanUtils Property Support

BeanUtils 支持的任何属性分配操作。 setProperty方法将在 Shiro 的[main]部分中起作用,包括 set/list/map 元素分配。有关更多信息,请参见Apache Commons BeanUtils 网站和文档。

字节数组值

由于原始字节数组不能以文本格式本地指定,因此我们必须使用字节数组的文本编码。这些值可以指定为 Base64 编码的字符串(默认值)或十六进制编码的字符串。默认值为 Base64,因为 Base64 编码需要较少的实际文本来表示值-它具有较大的编码字母,这意味着您的令牌更短(文本配置更好)。

# The 'cipherKey' attribute is a byte array.    By default, text values
# for all byte array properties are expected to be Base64 encoded:

securityManager.rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA==
...

但是,如果您更喜欢使用十六进制编码,则必须在 String 令牌前加上0x(“零”,“ x”):

securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008

Collection Properties

可以像设置其他任何属性一样直接设置列表,集合和 Map,也可以将其设置为嵌套属性。对于集和列表,只需指定一个以逗号分隔的值或对象引用集。

例如,一些 SessionListeners:

sessionListener1 = com.company.my.SessionListenerImplementation
...
sessionListener2 = com.company.my.other.SessionListenerImplementation
...
securityManager.sessionManager.sessionListeners = $sessionListener1, $sessionListener2

对于 Map,您可以指定键值对的逗号分隔列表,其中每个键值对均以冒号':'分隔。

object1 = com.company.some.Class
object2 = com.company.another.Class
...
anObject = some.class.with.a.Map.property

anObject.mapProperty = key1:$object1, key2:$object2

在上面的示例中,由$object1引用的对象将在 Map 中的字符串键key1下,即map.get("key1")返回object1。您还可以使用其他对象作为键:

anObject.map = $objectKey1:$objectValue1, $objectKey2:$objectValue2
...

Considerations

Order Matters

上面的 INI 格式和约定非常方便且易于理解,但是不如其他基于文本/ XML 的配置机制强大。使用上述机制时,最重要的要了解的是“Order 事项!”

Be Careful

每个对象实例化和每个值分配都按照它们在[main]部分中出现的 Sequences 执行。这些行最终转换为 JavaBeans getter/setter 方法调用,因此这些方法以相同的 Sequences 调用!

编写配置时请记住这一点。

Overriding Instances

任何对象都可以由稍后在配置中定义的新实例覆盖。因此,例如,第二个myRealm定义将覆盖第一个:

...
myRealm = com.company.security.MyRealm
...
myRealm = com.company.security.DatabaseRealm
...

这将导致myRealmcom.company.security.DatabaseRealm实例,并且以前的实例将永远不会被使用(并进行垃圾回收)。

Default SecurityManager

您可能已经在上面的完整示例中注意到,尚未定义 SecurityManager 实例的类,并且我们跳到了只设置嵌套属性的位置:

myRealm = ...

securityManager.sessionManager.globalSessionTimeout = 1800000
...

这是因为securityManager实例是一个特殊的实例-它已经为您实例化并可以使用,因此您无需知道要实例化的特定SecurityManager实现类。

当然,如果您实际上想要指定自己的实现,则可以按照上面“覆盖实例”部分中的说明定义实现:

...
securityManager = com.company.security.shiro.MyCustomSecurityManager
...

当然,这几乎是不需要的-Shiro 的 SecurityManager 实现是非常可定制的,通常可以配置任何必要的东西。您可能想问问自己(或用户列表)是否真的需要这样做。
users

[users]

[users] 部分允许您定义一组静态的用户帐户。这在用户帐户数量很少或不需要在运行时动态创建用户帐户的环境中最有用。这是一个例子:

[users]
admin = secret
lonestarr = vespa, goodguy, schwartz
darkhelmet = ludicrousspeed, badguy, schwartz

Automatic IniRealm

仅定义非空的[users]或[roles]部分将自动触发org.apache.shiro.realm.text.IniRealm实例的创建,并使其在 iniRealm 名称下的[main]部分中可用。您可以像上面描述的任何其他对象一样配置它。

Line Format

[users]部分中的每一行都必须符合以下格式:

username = password,* roleName1 roleName2 ,…, roleNameN *

  • 等号左侧的值是用户名

  • 等号右边的第一个值是用户的密码。必须 Importing 密码。

  • 密码后的任何逗号分隔值都是分配给该用户的角色的名称。角色名称是可选的。

Encrypting Passwords

如果您不希望[users]部分的密码为纯文本格式,则可以根据需要使用自己喜欢的哈希算法(MD5,Sha1,Sha256 等)对它们进行加密,并将结果字符串用作密码值。默认情况下,密码字符串应为十六进制编码,但可以将其配置为 Base64 编码(请参见下文)。

Easy Secure Passwords

为了节省时间并使用最佳实践,您可能需要使用 Shiro 的命令行哈希器,该设备将对密码以及任何其他类型的资源进行哈希处理。加密 INI [users]密码特别方便。

指定哈希文本密码值后,您必须告诉 Shiro 这些密码已加密。通过在[main]部分中隐式创建的iniRealm进行配置,以使用与您指定的哈希算法相对应的适当CredentialsMatcher实现:

[main]
...
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
...
iniRealm.credentialsMatcher = $sha256Matcher
...

[users]
# user1 = sha256-hashed-hex-encoded password, role1, role2, ...
user1 = 2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b, role1, role2, ...

您可以像其他任何对象一样在CredentialsMatcher上配置任何属性,以反映您的哈希策略,例如,指定是否使用盐析或要执行多少哈希迭代。请参阅org.apache.shiro.authc.credential.HashedCredentialsMatcher JavaDoc,以更好地了解哈希策略以及它们是否对您有用。

例如,如果用户的密码字符串是 Base64 编码的,而不是默认的十六进制,则可以指定:

[main]
...
# true = hex, false = base64:
sha256Matcher.storedCredentialsHexEncoded = false

[roles]

[roles] 部分允许您将Permissions与[users]部分中定义的角色相关联。同样,这在角色数量少的环境中或不需要在运行时动态创建角色的环境中很有用。这是一个例子:

[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# The 'schwartz' role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The 'goodguy' role is allowed to 'drive' (action) the winnebago (type) with
# license plate 'eagle5' (instance specific id)
goodguy = winnebago:drive:eagle5

Line Format

[角色]部分中的每一行都必须使用以下格式定义角色到权限的键/值 Map:

rolename = * permissionDefinition1 permissionDefinition2 ,..., permissionDefinitionN *

其中* permissionDefinition *是任意字符串,但是大多数人会希望使用符合条件的字符串
org.apache.shiro.authz.permission.WildcardPermission格式,以简化使用和灵 Active。有关权限以及如何从中受益的更多信息,请参见Permissions文档。

Internal commas

请注意,如果单个* permissionDefinition *需要在内部用逗号分隔(例如printer:5thFloor:print,info),则您需要在该定义前后加上双引号(“),以避免解析错误:"printer:5thFloor:print,info"

Roles without Permissions

如果您具有不需要权限关联的角色,则不需要,无需在[角色]部分中列出它们。如果角色名尚不存在,仅在[users]部分中定义角色名就足以创建角色。

[urls]

Web章节介绍了此部分及其选项。

协助处理文档

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

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