Apache Shiro 配置--官网

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 配置

SecurityManager的实现是模块化的,它包含了各种安全相关的组件,这些组件在SecurityManager中都有各自的getter和setter方法,所以可以轻松地配置SecurityManager及其内部对象。例如,如果想为SecurityManager配置一个自定义的SessionDAO来管理Session,可以直接通过setSessionDAO方法来设置:

DefaultSecurityManager securityManager = new DefaultSecurityManager(realm);

SessionDAO sessionDAO = new CustomSessionDAO();

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

直接调用各种方法,可以配置SecurityManager中的各种组件,虽然这种方式很简单,但它并不适用于绝大多数的真实应用环境中,原因有如下几点:

  • 它需要你了解SecurityManager并实例化SecurityManager的一个直接实现
  • 因为Java的类型安全的特性,你需要在调用get方法后将组件转换为它的具体实现类型,这样的转换是繁琐的,而且会和各个组件的具体实现造成紧耦合
  • 每当Shiro的配置改变的时候,都需要重新编译代码

然而,这种方法在内存受限的环境中仍然有用,比如智能手机应用程序。如果应用程序不在内存受限的环境中运行,那么基于文本的配置会更适合

通过INI配置文件配置

大多数应用程序都使用基于文本文件的配置,因为它可以较为清晰的描述代码之间的依赖关系,让那些不熟悉Shiro API的人更容易理解。为了保证一个具有最小第三方依赖的通用配置方式,Shiro支持使用INI格式来构建SecurityManager及其内部组件

从INI中创建一个SecurityManager

下面介绍从INI配置中构建SecurityManager的两种方式

从INI文件资源中构建SecurityManager

可以通过INI资源路径来创建一个SecurityManager实例,这个资源可以来自于文件系统,类路径或者URL,它们的前缀分别为"file:",“classpath:",”url:“。下面是通过向Factory注入一个类路径下的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

Shiro可以通过实例化org.apache.shiro.config.Ini来实例化一个INI配置,这个Ini类的功能类似于java.uril.Properties类,但它额外支持了section 的概念

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 配置

INI基本上是一个由唯一命名的小节组织的键/值对组成的文本配置。 键仅仅在每个小节中是唯一的,而不是整个配置,可以像单个属性定义一样查看每个部分。注释行以”#“或者”;“开头

下面是一个Shiro的INI配置文件的例子:

# =======================
# 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实例及其依赖的,通过键值对的形式,就可以在INI中完成配置。虽然这种方式不如XML强大,但是其简单易用,而且也不是很复杂。

下面是一个[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]
myRealm = com.company.shiro.realm.MyRealm
...

这一行实例化了一个com.company.shiro.relam.MyRealm,并以myRealm来命名,以后就可以通过myRealm来使用和配置这个对象。如果这个类实现了org.apache.shiro.util.Nameable接口,那么Nameable.setName方法将会被调用来设置实例的名字,就像这里的myRealm

设置对象的属性
字面量值

字面量值的属性可以直接用等号进行设置

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

上面的内容相当于调用了下面的代码

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

Shiro默认使用Apache Common BeanUtils来做这些设置工作,所以即使INI的值是文本类型的,BeanUtils也知道如何将字符串转换为合适的类型并调用正确的JavaBean的setter方法进行设置

引用类型值

如果需要设置一个引用类型的值的时候,可以使用"$"来引用一个之前定义过的实例

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

这里事先定义了一个名为sha256Matcher的对象,然后通过BeanUtils将其赋值给了myRealm实例(通过调用myRealm.setCredentialsMatcher(sha256Matcher)的方式)

嵌套属性

使用点分割的形式,可以将嵌套属性转换为对应的键字符串

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

上面的配置相当于调用了下面的代码

securityManager.getSessionManager().setGlobalSessionTimeout(1800000);
字节数组类型的值

因为原始的字节数组无法以文本形式表示,因此我们需要一个以文本形式表示字节数组的形式,如使用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==
...

如果使用十六进制编码的方式,则使用”0x“给字符串开头即可

securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008
集合类型的属性

List,Set和Map也可以像其他属性那样设置

对于Set和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

上述配置,即用字符串”key1“作为"object1"的键,map.get("key1")就会返回object1对象,如果键是引用类型的,可以使用下面的方式

anObject.map = $objectKey1:$objectValue1, $objectKey2:$objectValue2...
注意事项
顺序问题

在INI的每个小节中,每个配置是按顺序执行的,即后面的配置会覆盖掉前面的配置,比如下面的配置

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

最后myRealm将会是com.company.security.DatabaseRealm类型的

默认的SecurityManager

默认情况下,应用会有一个securityManager命名的SecurityManager实例,其类型为Shiro默认提供的org.apache.shiro.mgt.DefaultSecurityManager,如果你想使用自己的SecurityManager实现,可以像如下配置

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

多数情况下这是没有必要的,因为Shiro提供的DefaultSecurityManager已经适用于大多数环境下了

[users]

[user] 小节让你定义一个静态的用户集合,这是适用于用户数量小或者用户账户不需要在运行时动态创建时

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

如果在INI配置中配置了[users]或者[roles],且它们的内容不为空,则Shiro会自动创建一个org.apache.shiro.realm.text.IniRealm的实例,其命名为iniRealm,可以直接使用

格式说明

[users]中的每一行都有如下的形式

username = password, roleName1, roleName2, ..., roleNameN
  • 等号左边为用户名
  • 等号右边第一个值为密码
  • 等号右边除第一个值以外都是该用户拥有的角色名
密码加密

如果你不想密码是以明文的形式存在[users]小节,可以使用你想要的加密算法对密码进行加密,加密后的密码需要以十六进制字符串的形式呈现,也可以使用Base64的形式

可以使用Shiro提供的一个工具来快速的对明文密码进行加密,机密后产生的字符串可以直接用到[users]小节中

当你对密码进行加密后,需要配置一个CredentialsMatcher给Realm

[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配置任何属性值,如指定是否需要加盐加密,加密迭代几次哈希等

如果希望使用Base64编码的密文字符串,则可以通过如下配置指定

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

[roles]

[roles] 小节用来定义与权限相关的角色信息,同样的,这个方式适用于角色数量少且不需要在程序运行时改变的情况

[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]小节中每一行的格式如下

rolename = permissionDefinition1, permissionDefinition2,....

其中的permissionDefinition是一个字符串,多数情况下使用的是适用于org.apache.shiro.authz.permission.WildcardPermission的格式,因为它便于使用且灵活。有关premissionDefinition的详细信息会在后面的权限章节中说明

如果permissionDefinition中有逗号的话,则需要把这个permissionDefinition用双引号引起来,如printer:5thFloor:print,info,在写入配置文件中则需要变成"printer:5thFloor:print,info"

如果有一些不需要权限关联的角色,则可以在[roles]小节中不列出它们。如果角色还不存在,在[users]部分中定义角色名将会创建这个角色

[urls]

[urls]小节的内容将会在Web章节中介绍

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容