1、在web环境下使用Shiro使用
web
将 Shiro 集成到任何 Web 应用程序的最简单的方法是在 web.xml 中配置 ContextListener 和 Filter,理解如何读取 Shiro 的 INI 配置文件。算法
一、web.xml配置shell
<!-- Shiro --> <listener> <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class> </listener> <filter> <filter-name>ShiroFilter</filter-name> <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class> </filter> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> <dispatcher>ERROR</dispatcher> </filter-mapping>
这假设一个 Shiro INI Configuration 文件在如下两个位置任意一个,并使用最早发现的那个:apache
/WEB-INF/shiro.ini数组
在classpath 根目录下shiro.ini 文件安全
下面是上述配置所作的事情:session
EnvironmentLoaderListener 初始化一个Shiro WebEnvironment 实例(其中包含 Shiro 须要的一切操做,包括 SecurityManager ),使得它在 ServletContext 中可以被访问。若是你须要在任什么时候候得到WebEnvironment 实例,你能够调用WebUtils.getRequiredWebEnvironment(ServletContext)。app
ShiroFilter 将使用此 WebEnvironment 对任何过滤的请求执行全部必要的安全操做。ide
最后,filter-mapping 的定义确保了全部的请求被 ShiroFilter 过滤,建议大多数 Web 应用程序使用以确保任何请求是安全的。ui
ShiroFilter filter-mapping
它一般可取的作法是在任何其余 filter-mapping 声明以前定义 ShiroFilter filter-mapping,以确保 Shiro 也能在那些过滤器 下工做的很好。
IniWebEnvironment 将会去读取和加载 INI 配置文件。默认状况下,这个类会自动地在下面两个位置寻找 Shiro.ini 配置(按顺序)。
/WEB-INF/shiro.ini
classpath:shiro.ini
它将使用最早发现的那个。 然而,若是你想把你的配置放在另外一位置,你能够在 web.xml 中用contex-param 指定该位置。
<context-param>
<param-name>shiroConfigLocations</param-name>
<param-value>YOUR_RESOURCE_LOCATION_HERE</param-value>
</context-param>
默认状况下,在 ServletContext.getResource 方法定义的规则下,param-value 是能够被解析的。例如, /WEB-INF/some/path/shiro.ini。
3、INI Configuration配置
为了确保具备共性的基于文本配置的途径适用于任何环境并且减小对第三方的依赖,Shiro 支持使用 INI 建立 SecurityManager 对象视图(graph)以及它支持的组件,INI 易读易配置,很容易建立而且对大多数程序都很适合。
INI 基于文本配置,在独立命名的区域内经过成对的键名/键值组成。键名在每一个区域内必须惟一,但在整个配置文件中并不须要这样(这点和JDK的Properties不一样),每个区域(section)能够看做是一个独立的Properties 定义。
这里是一个 Shiro 能够理解的各 section 的示例。
# ======================= # 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。
经过INI配置像 SecurityManager 的对象实例及其支撑组件听起来是一件很困难的事情,由于在这里咱们只能用键名/键值对。但经过定义一些对象视图(graphs)能够理解的惯例,你发现你彻底能够这样作。Shiro 利用这些假定的惯例来实现一个简单而简明的配置途径。
咱们常常将这种方法认为是“可怜人的(poor man's)”的依赖注入,虽然不及成熟的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
Defining an object 定义一个对象
在[main]中包含如下片断。
[main]
myRealm = com.company.shiro.realm.MyRealm ...
这一行实例化了一个类型为 com.company.shiro.realm.MyRealm 的对象实例而且使对象使用 myRealm 做为名称以便于未来引用和配置。
若是对象实例化时实现了 org.apache.shiro.util.Nameable 接口,Nameable.setName方法将被以该名(在此例中为myRealm)命名的对象调用。
Setting object properties 设置对象属性
Primitive Values 原始值
简单的原始值属性可使用下面的等于符号进行设置:
... myRealm.connectionTimeout = 30000 myRealm.username = jsmith ...
这些配置行转换为方法调用就是:
... myRealm.setConnectionTimeout(30000); myRealm.setUsername("jsmith"); ...
怎么作到的呢?它假定全部对象都是兼容 JavaBean 的 POJO。在设置这些属性时,Shiro 默认使用 Apache 通用的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 嵌套属性
经过在等号左侧使用点符号,你能够获得你但愿设置对象视图最终的对象/属性,例以下面这行配置:
... securityManager.sessionManager.globalSessionTimeout = 1800000 ...
转换逻辑为(经过BeanUtils):
securityManager.getSessionManager().setGlobalSessionTimeout(1800000);
用这种方法访问的层数须要多深能够有多深: object.property1.property2....propertyN.value = blah
BeanUtils 属性支持
BeanUtils 支持任何指定的属性操做,在 Shiro [main] 区域中setProperty方法将被调用,包括集合(set)/列表(list)/图(map),查看Apache Commons BeanUtils Website和文档了解更多的信息。
Byte Array Values 字节数组值
由于原始的字节数组不能直接在文本中定义,咱们必须使用字节数组的文本编码。可使用64位编码(默认)或者16位编码,默认为64位编码由于使用64位编码实际文字会少一些--它拥有很大的编码表,这意味着你的标识会更短(对于文本配置来说会好一些)。
# 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== ...
若是你想使用16位编码,你必须在字串前面加上 0x 前缀:
securityManager.rememberMeManager.cipherKey = 0x3707344A4093822299F31D008
Collection Properties 集合属性
列表(Lists)、集合(Sets)、图(Maps)能够像其它属性同样设置--直接设置或者像嵌套属性同样,对于列表和集合,只需指定一个逗号分割的值集或者对象引用集。
如定义一些SessionListeners:
sessionListener1 = com.company.my.SessionListenerImplementation ... sessionListener2 = com.company.my.other.SessionListenerImplementation ... securityManager.sessionManager.sessionListeners = $sessionListener1, $sessionListener2
对于图(Maps),你能够指定以逗号分割的键-值对列表,每一个键-值之间用冒号分割
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 ...
Considerations 注意事项
Order Matters 顺序问题
上述 INI 格式和约定很是方便也很是易懂,但它并无另一种 text/XML的配置路径强大,经过上述途径进行配置须要知道很是重要的一件事情就是顺序问题!
当心
每个对象实例以及每个指定的值都将按照其在 [main] 区域中产生的顺序的执行,这些行最终转换为 JavaBeans 的 getter/setter 方法调用,这些方法按一样的顺序调用。
当你写配置文件的时候要牢记于此。
Overriding Instances 覆盖实例
每个对象均可以被后定义的新实例覆盖,例如,第二个myRealm定义将重写第一个:
... myRealm = com.company.security.MyRealm ... myRealm = com.company.security.DatabaseRealm ...
这样的结果是 myRealm 是 com.company.security.DatabaseRealm 实例而前面的实例不会被使用(会做为垃圾回收)。
Default SecurityManager 默认Default SecurityManager
你可能注意到在以上全部例子中都没有定义 SecurityManager,而咱们直接设置其嵌套属性
myRealm = ... securityManager.sessionManager.globalSessionTimeout = 1800000 ...
这是由于securityManager实例是特殊的--它已经为你实例化过了而且准备好了,因此你并不须要知道指定的实例化SecurityManager的实现类。
固然,若是你确实想指定你本身的实现类,你能够像上面的覆盖实例那样定义你本身的实现:
... securityManager = com.company.security.shiro.MyCustomSecurityManager ...
固然,不多须要这样--Shiro 的 SecurityManager 实现能够按需求进行定制,你可能要问一下本身(或者用户群)你是否真的须要这样作。
[users]
[users]区域容许你定义一组静态的用户账号,这对于那些只有少数用户账号而且用户账号不须要在运行时动态建立的环境来讲很是有用。下面是一个例子:
[users]
admin = secret lonestarr = vespa, goodguy, schwartz darkhelmet = ludicrousspeed, badguy, schwartz
自动生成IniRealm
定义非空的[users]或[roles]区域将自动建立org.apache.shiro.realm.text.IniRealm 实例,在[main]区域下生成一个可用的 iniRealm ,你能够像上面配置其它对象那样配置它。
Line Format 格式
[users]区域下每一行必须和下面的形式一致:
username = password, roleName1, roleName2, ..., roleNameN
等号左边的值是用户名;
等号右侧第一个值是用户密码,密码是必须的;
密码以后用逗号分割的值是赋予用户的角色名,角色名是可选的。
Encrypting Passwords 密码加密
若是你不但愿[users]区域下的密码以明文显示,你能够用你喜欢的哈希算法(MD5, Sha1, Sha256, 等)来加密它们,将加密后的字符串做为密码值,默认的,密码建议用16位编码算法,但也能够用64位编码算法替代(以下)
简单的安全密码
为了节约时间得到最佳实践,你可使用 Shiro 的 Command Line Hasher,它能够加密密码和其它类型的资源,尤为使给 INI[user] 密码加密变得很是简单。
一旦你指定了加密后的密码值,你必须告诉 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 的全部属性,例如,指定使用salting或者有多少hash iterations执行,能够查看org.apache.shiro.authc.credential.HashedCredentialsMatcher Java文档更好地理解 hashing 策略,可能会颇有帮助。
例如,若是你用64位编码方式取代了16位编码方式,你应该指定:
[main]
... # true = hex, false = base64: sha256Matcher.storedCredentialsHexEncoded = false
[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
Line Format 格式
[roles]区域下的每一行必须用下面的格式定义角色-权限的键/值对应关系。
rolename = permissionDefinition1, permissionDefinition2, ..., permissionDefinitionN
权限定义能够是很是随意的字符串,但大部分用户仍是但愿使用易用而灵活的和org.apache.shiro.authz.permission.WildcardPermission形式一致的字符串格式。查看 Permissions 文档获取更多关于权限的信息和你能够如何利用它为你服务。
内部用法
注意若是一个特定的权限定义须要用到逗号分隔(如:printer:5thFloor:print,info),你须要将该定义用双引号括起来从而避免出错:"printer:5thFloor:print,info"。
没有权限的角色
若是你有不须要权限的角色,不须要将它们列入[roles]区域,仅仅在 [users]区域定义角色名就能够建立它们(若是它们尚不存在)。
[urls]
在urls 项的每一行格式以下:
URL_Ant_Path_Expression = Path_Specific_Filter_Chain