首页 > 代码库 > Apache Shiro系列之五:配置
Apache Shiro系列之五:配置
Shiro设计的初衷就是可以运行于任何环境:无论是简单的命令行应用程序还是复杂的企业集群应用。由于运行环境的多样性,所以有多种配置机制可用于配置,本节我们将介绍Shiro内核支持的这几种配置机制。
小贴士:多种配置方案:
Shiro的SecurityManager是和JavaBean兼容的,所以我们可以使用诸如Java、Xml(Spring、Jboss、Guice等)、YAML、Json、Groovy等配置方式。
一、基于Java代码的配置
最简单的创建并且使用SecurityManager的方式就是直接在代码中创建org.apache.shiro.mgt.DefaultSecurityManager类实例,比如:
1 Realm realm =//instantiate or acquire a Realm instance. We‘ll discuss Realms later.2 SecurityManager securityManager =newDefaultSecurityManager(realm);3 //Make the SecurityManager instance available to the entire application via static memory:4 SecurityUtils.setSecurityManager(securityManager);
只需区区三行代码,我们就已经为任何类型的应用程序配置好了一个全功能的Shiro运行环境,你看,多简单。
SecurityManager对象图谱:
就像我们在架构一节中介绍的,SecurityManager的实现是模块化的,而且可以兼容JavaBean,所以你可以通过setter和getter方法来配置SecurityManager及其内部组件。
比如如果你想把一个自定义的SessionDAO配置为SecurityManager的Session管理器,你可以直接调用SessionManager的setSessionDAO方法。
...DefaultSecurityManager securityManager =newDefaultSecurityManager(realm);SessionDAO sessionDAO =newCustomSessionDAO();((DefaultSessionManager)securityManager.getSessionManager()).setSessionDAO(sessionDAO);...
你可以通过这种调用setter方法的方式来设置SecurityManager的任何内置组件。但是对于现实的应用程序来说,这不是一种理想的配置方式。主要有以下几点原因:
#,这种直接编码的方式要求我们知道这个具体的实现类在哪,并且要自己去创建他。而我们一般建议是依赖于抽象而不是具体,所以最好不要让我知道他具体的实现在哪里。
#,由于java的类型安全特性,当我们通过getter方法获取到某个类的具体实现之后,我们将不得不把他们强制类型转换为具体的类型,如此多的强制类型转换太丑了,不是一种好的编程实践。
#,如果我们通过SecurityUtils.setSecurityManager方法为当前的应用设置一个虚拟机范围内的静态SecurityManager对象,在大多数应用中都是ok的。但是如果我们要在一个虚拟机上运行多个使用Shiro的应用程序时,就可能会出乱子了。所以如果能够为每个应用程序创建一个的单例就更好了;
#,每次你要修改一下Shiro的配置都不得不重新编译程序;
虽然有以上提到的种种缺点,但是如果你要在一个内存受限的环境(比如智能手机)中使用Shiro,使用基于java代码的配置还是不错的选择。而如果内存不太受限的话,使用推荐使用基于文本的配置,因为他对用户更友好,具有更好的可读性。
二、INI配置
为了让这个文本配置方案能够在所有的开发环境中使用,并且尽可能的减少对于第三方工具的依赖,我们选择了INI格式来配置SecurityManager及其相关组件。INI具有易读、易配置的特性,可以适用于绝大多数的应用。
(一)从INI文件中创建一个SecurityManager
一下将提供两种基于INI配置文件创建SecurityManager的方法。
从INI资源文件中创建SecurityManager
我们可以通过一个INI资源的路径来创建一个SecurityManager,资源可以通过文件系统、classpath、或者url中获取,不同的获取方式需要在资源路径前加不同的前缀,分别是file:, classpath 或者 url:,下面这个例子我们使用一个工厂类从根classpath中找到shiro.ini文件,然后实例化了一个SecurityManager对象。
1 import org.apache.shiro.SecurityUtils;2 import org.apache.shiro.util.Factory;3 import org.apache.shiro.mgt.SecurityManager;4 import org.apache.shiro.config.IniSecurityManagerFactory;5 ...6 Factory<SecurityManager> factory =newIniSecurityManagerFactory("classpath:shiro.ini");7 SecurityManager securityManager = factory.getInstance();8 SecurityUtils.setSecurityManager(securityManager);
从INI类实例中创建SecurityManager
如果有需要,我们也可以通过org.apache.shiro.config.Ini类来做INI配置,这个Ini类的API和java.util.Properties类比较像,只是在接口中需要传入Section的名称。
例如:
1 import org.apache.shiro.SecurityUtils; 2 import org.apache.shiro.util.Factory; 3 import org.apache.shiro.mgt.SecurityManager; 4 import org.apache.shiro.config.Ini; 5 import org.apache.shiro.config.IniSecurityManagerFactory; 6 ... 7 Ini ini =newIni(); 8 //populate the Ini instance as necessary 9 ...10 Factory<SecurityManager> factory =newIniSecurityManagerFactory(ini);11 SecurityManager securityManager = factory.getInstance();12 SecurityUtils.setSecurityManager(securityManager);
以上我们已经知道如何通过INI配置来实例化SecurityManager对象,下面就让我们来看看一个真实的Shiro的配置文件到底长什么样。
(二) INI Secions
所谓的INI文件其实就是按Section分隔的键值对的集合,不同的Section名称不同,每个Section内的键名称要有唯一性。每个Section可以看做就是一个Properties。
注释行可以井号(#) 开头,也可以 分号(;)开头。、
下面就是一个Shiro能够解析的INI文件的例子,他的这些Section名称是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及其他的依赖项,比如Realms。
要使用INI这种简单键值对文件格式来配置SecurityManager对象及其依赖项这种层级关系感觉难度有点大,只要稍微加上一些约定,你会发现INI文件能做的远比我们想象的要多,我们把这种基于INI的配置叫做“穷人”的依赖注入。虽然没有Spring/JBoss之类的高富帅那么强大,但是已经足够满足Shiro的配置要求了。下面是一个[main]段的配置例子,我们会在下文详细解释,不过我估计在解释之前,你也能猜个八九不离十了。
[main]sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatchermyRealm = com.company.security.shiro.DatabaseRealmmyRealm.connectionTimeout =30000myRealm.username = jsmithmyRealm.password = secretmyRealm.credentialsMatcher = $sha256MatchersecurityManager.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接口。
设置对象属性
基本类型
基本类型属性可以像下面这样直接赋值。
...myRealm.connectionTimeout =30000myRealm.username = jsmith...
这段配置翻译成Java代码后是这样的:
1 ...2 myRealm.setConnectionTimeout(30000);3 myRealm.setUsername("jsmith");4 ...
这是如何做到的呢? 这里嘉定所有的对象都是和Java Bean兼容的POJO对象。
在这种约定的前提下,当给对象设置属性时,Shiro会将所有的脏活、累活都交给Apache Common BeanUtils来干,虽然我们在INI文件内配置的是文本,但是BeanUtils知道如何将一个字符串的值转换为基本类型,并且调用该对象的对应的setter方法来给该POJO设置属性。
引用类型
如果要设置是引用类型怎么办?你可以用一个美元符号来引用前文中定义的对象,像下面这样,就这么简单。
1 ...2 sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher3 ...4 myRealm.credentialsMatcher = $sha256Matcher5 ...
嵌套属性
你可以像下面这样引用嵌套属性,给属性赋值,不管有多少个层级,都可以这么用。
...securityManager.sessionManager.globalSessionTimeout =1800000...
BeanUtils会把他翻译成下面这样的代码:
1 securityManager.getSessionManager().setGlobalSessionTimeout(1800000);
这种嵌套的层级可以要多深有多深,比如:
object.property1.property2.....propertyN.value=http://www.mamicode.com/blah
小贴士:BeanUtil支持的属性设置
只要是BeanUtil支持的属性设置方式你都可以在Shiro配置文件的[main] Section中配置。包括对于set/list/map属性的设置。详情可以参考 Apache Commons BeanUtils Website官方文档。
字节数组
因为在文本文件中无法直接表示二进制树,所以必须使用一种可以使用文本编码的方式来表示二进制数组,有两种选择,一种是:BASE64,一种是十六进制字符串。默认使用BASE64编码,因为表示相同长度的二进制,BASE64需要的字节更少。这显然更合适文本配置。
#The‘cipherKey‘ attribute is a byte array.Bydefault, 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类型的属性,不管是直接的属性还是嵌套属性。对于list和set,我们可以直接使用逗号分隔的值(或者引用)来设置,例如:
sessionListener1 = com.company.my.SessionListenerImplementation...sessionListener2 = com.company.my.other.SessionListenerImplementation...securityManager.sessionManager.sessionListeners = $sessionListener1, $sessionListener2
对于map,你可以指定一系列都好分隔的键值对。键值对内部使用 冒号来作为键和值的分隔符,例如:
object1 = com.company.some.Classobject2 = com.company.another.Class...anObject = some.class.with.a.Map.propertyanObject.mapProperty = key1:$object1, key2:$object2
你还可以直接使用对象来作为key,如下:
anObject.map = $objectKey1:$objectValue1, $objectKey2:$objectValue2
一些思考
顺序问题
在INI文件中配置的顺序决定了他们翻译成Java代码之后的顺序,这块要小心。
重写实例
后定义的同名对象会覆盖之前定义的对象,如下第二个myRealm会覆盖掉第一个myRealm的定义,所以最终myRealm是com.company.security.DatabaseRealm的对象实例,而前一个定义的realm将永远不会被引用了。(自然也就被垃圾回收了)。
...myRealm = com.company.security.MyRealm...myRealm = com.company.security.DatabaseRealm...
默认的SecurityManager
你可能已经注意到,在上文的配置中,我们并没有创建SecurityManager对象就直接去设置他的嵌套属性了。
myRealm =...securityManager.sessionManager.globalSessionTimeout =1800000...
SecurityManager是特例,系统已经自动为你创建好了一个SecurityManager对象,你就只管用就好了。当然,如果你说我一定要使用字节实现的一个SecurityManager,那也不是不行,直接像下面这么写就可以了,就像 重写实例 章节中说的,这样就会覆盖掉系统自动创建的SecurityManager对象了。
...securityManager = com.company.security.shiro.MyCustomSecurityManager...
当然,这种情况很少发生。因为Shiro默认提供的SecurityManager具有很好的定制性,你几乎可以为配置任何属性。所以如果你发现自己要写一个自定义的SecurityManager的时候,不妨先问问自己:这真的有必要吗?
(2) [users]
[users]Setion允许你定义一组用户账号。在那些只需要很少的用户账号,或者是那些不需要在运行时动态创建用户账号的运行环境下,这个功能很实用。你可以像下面这么写。
[users]admin = secretlonestarr = vespa, goodguy, schwartzdarkhelmet = ludicrousspeed, badguy, schwartz
小贴士:自动生成的IniRealm
一旦Shiro发现INI文件的[users], [roles]章节不为空,他就会自动创建一个org.apache.shiro.realm.text.IniRealm类的实例并命名为iniRealm,所以你可以在[main]章节中像配置其他对象一样给iniRealm配置属性。
行格式
在[users]的每一行将定义成如下格式
username=password, roleName1, roleName2, ..., roleNameN
#,左边的key是用户名;
#,右边是逗号分隔的密码和角色,其中第一个而是密码,后续的是角色名,角色可以有多个;
#,角色是可选的;
密码加密
如果你不想让密码直接用明文显示,也可以使用任何你喜欢的加密算法(MD5,Sha1,Sha256, 等等)来对密码做加密,然后把加密之后的文本复制到INI文件中。加密之后的密码默认应该以十六进制的文本,当然也可以是BASE64的,具体看下问的说明。
小贴士:简单安全的密码
对密码做加密的最佳实践是使用Shiro提供的 Command Line Hasher工具,他会对密码或者其他你想要加密的文本做哈希,这个工具对于要放在Shiro的INI配置文件的[users]中显示的密码最为实用。
如果你对密码做了加密,那么你就要告诉Shiro你对密码做了加密,不然他就不认识了。你可以在[main]端中配置Shiro隐式生成的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,...
和其他属性一样,你可以给CredentialMatcher配置任何值来体现你的哈希策略,比如指定一个盐值,或者是哈希迭代的次数。要想更好的了解哈希策略,你可以查看
org.apache.shiro.authc.credential.HashedCredentialsMatcher
的API文档。比如,如果用户的密码是BASE64编码,而不是默认的16进制,则应该像下面这么配置。
[main]...#true= hex,false= base64:sha256Matcher.storedCredentialsHexEncoded =false
(3)[roles]
这个session用于定义角色与权限的对照关系,同样的,这种配置方式对于那些只有少数几种角色,并且不需要在运行时动态创建角色的应用程序特别实用。
[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,..., permissionDefinitionN
此处permissionDefinition 可以是任意文本,不过一般我们会建议使用和
org.apache.shiro.authz.permission.WildcardPermission
格式兼容的文本格式,这种格式简单而又灵活。可以查看权限( Permissions)章节来了解更多关于这种权限格式的信息。 小贴士:权限内部的分号
如果在权限内部要有分号,则在角色定义中必须要用双引号将他们括起来,以避免解析错误,比如printer:5thFloor:print,info,就要写成“printer:5thFloor:print,info”
小贴士:不需要权限的角色定义
如果你的角色是不需要关联权限的,那么你不需要在[roles]章节中来列出他们,你尽管在[users]章节中使用他们就是了。如果系统中不存在这个角色,则Shiro会自动创建对应的角色。
(4)[urls]
这个Section的描述将放在 Web 章节。
原文地址:http://shiro.apache.org/configuration.html
本系列相关:
Apache Shiro系列一:初识
Apache Shiro系列二,基本概念
Apache Shiro系列三:10分钟入门
Apache Shiro系列四:Shiro的架构
Apache Shiro系列之五:配置
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。