Apache Shiro(四)shiro配置

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

程序化配置

创建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实现本质上是嵌套安全特定组件的模块化对象。因为它们也是JavaBeans兼容的,所以可以调用任何嵌套的组件,通过gettersetter方法来配置SecurityManager它及其内部对象关系。

...

DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);

SessionDAO sessionDAO = new CustomSessionDAO();

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

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

但是,尽管程序化配置很简单,但是对于大部分真实的应用来说,它并理想配置。编程配置可能不适合您的应用程序有几个原因:

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

  • 由于Java的类型安全性,您需要将通过get*方法获得的对象转换为其特定的实现。这些强制转换是丑陋的,冗长的,并且将你与实现课程紧密结合在一起。

  • 通过SecurityUtils.setSecurityManager方法调用创建的,SecurityManager实例在JVM是静态的,单例的。这会使用该对象能被同一JVM中的多个应用公用。如果超过一个启用shiro的应用程序是在运行同一个JVM上会造成问题。如果实例是应用程序单例,但不是静态内存引用可能会更好。

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

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

INI配置

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

为了确保shiro可以在具有最小第三方依赖性的环境中工作,Shiro支持INI格式来构建SecurityManager对象关系网及其支持组件。INI易于阅读,易于配置,易于设置,适合大多数应用。

1、从INI创建SecurityManager

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

来自INI资源的SecurityManager

我们可以从INI资源路径创建SecurityManager实例。当前缀为,或分别为前缀时file:,可以从文件系统,类路径或URL获取资源。此示例使用工厂从类路径的根目录中提取文件并返回实例:classpath:url:Factoryshiro.iniSecurityManager

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类类似,但还支持按节名称进行分段。

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部分

 

INI基本上是一个文本配置,由键/值对组成,且键名在每个部分是唯一,而不是在整个配置上(这与JDK 属性不同)。每个部分可以被视为单个定义。

注释行由#开始或者分号(';')

以下是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

1、[main]部分

[main]部分是您配置应用程序SecurityManager实例及其任何依赖项的部分,例如Realm

配置像SecurityManager或其任何依赖项之类的对象实例听起来与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接口,则将调用Nameable.setName为其设置实例名(在此示例中实例名为myRealm)。

设置对象属性

基本类型的值

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

等价于

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

事实上,Shiro默认使用Apache Commons BeanUtils来完成设置这些属性。因此,尽管INI值是文本,但BeanUtils知道如何将字符串值转换为正确的基本类型,然后调用相应的JavaBeans setter方法。

设置引用属性

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

 

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

设置嵌套属性

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

等价于
securityManager.getSessionManager().setGlobalSessionTimeout(1800000);

设置字节数组值

因为原始字节数组不能以文本格式来描述,所以我们必须使用字节数组的文本编码。可以将值指定为Base64编码的字符串(默认值)或Hex编码的字符串。默认值为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

securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008

设置集合属性

 list

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将位于key1键下的映射中,即map.get("key1")返回object1。您还可以使用其他对象作为键

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

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

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

覆盖实例

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

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

这将导致myRealm成为一个com.company.security.DatabaseRealm实例,并且永远不会使用前一个实例(并收集垃圾)。

默认SecurityManager

您可能已经在上面的完整示例中注意到,我们未定义SecurityManager实例的类,但是可以直接跳转到仅设置嵌套属性:

myRealm = ...

securityManager.sessionManager.globalSessionTimeout = 1800000
...

这是因为securityManager实例是一个特殊的实例 - 它已经为您实例化并准备好了,所以您不需要知道SecurityManager要实例化的特定实现类。

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

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

当然,这很少需要。

2、[users]

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

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

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

行格式

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

usernamepasswordroleName1roleName2,...,roleNameN

  • 等号左侧的值是用户名
  • 等号右侧的第一个值是用户的密码。密码是必需的。
  • 密码后面的任何逗号分隔值是分配给该用户的角色的名称。角色名称是可选的。

加密密码

如果您不希望[users]部分密码为纯文本,您可以使用您喜欢的哈希算法(MD5,Sha1,Sha256等)加密它们,但是您喜欢并使用生成的字符串作为密码值。默认情况下,密码字符串应为十六进制编码,但可以配置为Base64编码(见下文)。

一旦指定了散列文本密码值,就必须告诉Shiro这些是加密的。您可以通过配置iniRealm[main]部分中隐式创建的方法来使用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, ...

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

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

3、[roles]

[roles]部分允许您将权限与[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

行格式

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

rolenamepermissionDefinition1permissionDefinition2,...,permissionDefinitionN

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

如果您的角色不需要权限关联,则不需要在[roles]部分中列出它们。只是在[users]部分中定义角色名称就足以创建角色(如果它尚不存在)。

[urls]

本章及其选项在其他章节中进行了描述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值