- 浏览: 600283 次
- 性别:
- 来自: 厦门
文章分类
- 全部博客 (669)
- oracle (36)
- java (98)
- spring (48)
- UML (2)
- hibernate (10)
- tomcat (7)
- 高性能 (11)
- mysql (25)
- sql (19)
- web (42)
- 数据库设计 (4)
- Nio (6)
- Netty (8)
- Excel (3)
- File (4)
- AOP (1)
- Jetty (1)
- Log4J (4)
- 链表 (1)
- Spring Junit4 (3)
- Autowired Resource (0)
- Jackson (1)
- Javascript (58)
- Spring Cache (2)
- Spring - CXF (2)
- Spring Inject (2)
- 汉字拼音 (3)
- 代理模式 (3)
- Spring事务 (4)
- ActiveMQ (6)
- XML (3)
- Cglib (2)
- Activiti (15)
- 附件问题 (1)
- javaMail (1)
- Thread (19)
- 算法 (6)
- 正则表达式 (3)
- 国际化 (2)
- Json (3)
- EJB (3)
- Struts2 (1)
- Maven (7)
- Mybatis (7)
- Redis (8)
- DWR (1)
- Lucene (2)
- Linux (73)
- 杂谈 (2)
- CSS (13)
- Linux服务篇 (3)
- Kettle (9)
- android (81)
- protocol (2)
- EasyUI (6)
- nginx (2)
- zookeeper (6)
- Hadoop (41)
- cache (7)
- shiro (3)
- HBase (12)
- Hive (8)
- Spark (15)
- Scala (16)
- YARN (3)
- Kafka (5)
- Sqoop (2)
- Pig (3)
- Vue (6)
- sprint boot (19)
- dubbo (2)
- mongodb (2)
最新评论
什么是Apache Shiro
Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。
记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:
1.认证 - 用户身份识别,常被称为用户“登录”;
2.授权 - 访问控制;
3.密码加密 - 保护或隐藏数据防止被偷窥;
4.会话管理 - 每个用户相关的时间敏感的状态。
Shiro还支持一些辅助特性,如Web应用安全、单元测试和多线程,它们的存在强化了上面提到的四个要素。
为何要创建Apache Shiro
对于一个框架来讲,使其有存在价值的最好例证就是有让你去用它的原因,它应该能完成一些别人无法做到的事情。要理解这一点,需要了解Shiro的历史以及创建它时的其他替代方法。
在2008年加入Apache软件基金会之前,Shiro已经5岁了,之前它被称为JSecurity项目,始于2003年初。当时,对于Java应用开发人员而言,没有太多的通用安全替代方案 - 我们被Java认证/授权服务(或称为JAAS)紧紧套牢了。JAAS有太多的缺点 - 尽管它的认证功能尚可忍受,但授权方面却显得拙劣,用起来令人沮丧。此外,JAAS跟虚拟机层面的安全问题关系非常紧密,如判断JVM中是否允许装入一个类。作为应用开发者,我更关心应用最终用户能做什么,而不是我的代码在JVM中能做什么。
由于当时正从事应用开发,我也需要一个干净、容器无关的会话机制。在当时,“这场游戏”中唯一可用的会话是HttpSessions,它需要Web容器;或是EJB 2.1里的有状态会话Bean,这又要EJB容器。而我想要的一个与容器脱钩、可用于任何我选择的环境中的会话。
最后就是加密问题。有时,我们需要保证数据安全,但是Java密码架构(Java Cryptography Architecture)让人难以理解,除非你是密码学专家。API里到处都是Checked Exception,用起来很麻烦。我需要一个干净、开箱即用的解决方案,可以在需要时方便地对数据加密/解密。
于是,纵观2003年初的安全状况,你会很快意识到还没有一个大一统的框架满足所有上述需求。有鉴于此,JSecurity(即之后的Apache Shiro)诞生了。
今天,你为何愿意使用Apache Shiro
从2003年至今,框架选择方面的情况已经改变了不少,但今天仍有令人信服的理由让你选择Shiro。其实理由相当多,Apache Shiro:
易于使用 - 易用性是这个项目的最终目标。应用安全有可能会非常让人糊涂,令人沮丧,并被认为是“必要之恶”【译注:比喻应用安全方面的编程。】。若是能让它简化到新手都能很快上手,那它将不再是一种痛苦了。
广泛性 - 没有其他安全框架可以达到Apache Shiro宣称的广度,它可以为你的安全需求提供“一站式”服务。
灵活性 - Apache Shiro可以工作在任何应用环境中。虽然它工作在Web、EJB和IoC环境中,但它并不依赖这些环境。Shiro既不强加任何规范,也无需过多依赖。
Web能力 - Apache Shiro对Web应用的支持很神奇,允许你基于应用URL和Web协议(如REST)创建灵活的安全策略,同时还提供了一套控制页面输出的JSP标签库。
可插拔 - Shiro干净的API和设计模式使它可以方便地与许多的其他框架和应用进行集成。你将看到Shiro可以与诸如Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin这类第三方框架无缝集成。
支持 - Apache Shiro是Apache软件基金会成员,这是一个公认为了社区利益最大化而行动的组织。项目开发和用户组都有随时愿意提供帮助的友善成员。像Katasoft这类商业公司,还可以给你提供需要的专业支持和服务。
谁在用Shiro
Shiro及其前身JSecurity已被各种规模和不同行业的公司项目采用多年。自从成为Apache软件基金会的顶级项目后,站点流量和使用呈持续增长态势。许多开源社区也正在用Shiro,这里有些例子如Spring,Grails,Wicket,Tapestry,Tynamo,Mule和Vaadin。
如Katasoft,Sonatype,MuleSoft这样的商业公司,一家大型社交网络和多家纽约商业银行都在使用Shiro来保护他们的商业软件和站点。
核心概念:Subject,SecurityManager和Realms
既然已经描述了Shiro的好处,那就让我们看看它的API,好让你能够有个感性认识。
Shiro架构有三个主要概念 - Subject,SecurityManager和Realms。
Subject
在考虑应用安全时,你最常问的问题可能是“当前用户是谁?”或“当前用户允许做X吗?”。当我们写代码或设计用户界面时,问自己这些问题很平常:应用通常都是基于用户故事构建的,并且你希望功能描述(和安全)是基于每个用户的。所以,对于我们而言,考虑应用安全的最自然方式就是基于当前用户。Shiro的API用它的Subject概念从根本上体现了这种思考方式。
Subject一词是一个安全术语,其基本意思是“当前的操作用户”。称之为“用户”并不准确,因为“用户”一词通常跟人相关。在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。在代码的任何地方,你都能轻易的获得Shiro Subject,参见如下代码:
清单1. 获得Subject
一旦获得Subject,你就可以立即获得你希望用Shiro为当前用户做的90%的事情,如登录、登出、访问会话、执行授权检查等 - 稍后还会看到更多。这里的关键点是Shiro的API非常直观,因为它反映了开发者以‘每个用户’思考安全控制的自然趋势。同时,在代码的任何地方都能很轻松地访问Subject,允许在任何需要的地方进行安全操作。
SecurityManager
Subject的“幕后”推手是SecurityManager。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。它是Shiro框架的核心,充当“保护伞”,引用了多个内部嵌套安全组件,它们形成了对象图。但是,一旦SecurityManager及其内部对象图配置好,它就会退居幕后,应用开发人员几乎把他们的所有时间都花在Subject API调用上。
那么,如何设置SecurityManager呢?嗯,这要看应用的环境。例如,Web应用通常会在Web.xml中指定一个Shiro Servlet Filter,这会创建SecurityManager实例,如果你运行的是一个独立应用,你需要用其他配置方式,但有很多配置选项。
一个应用几乎总是只有一个SecurityManager实例。它实际是应用的Singleton(尽管不必是一个静态Singleton)。跟Shiro里的几乎所有组件一样,SecurityManager的缺省实现是POJO,而且可用POJO兼容的任何配置机制进行配置 - 普通的Java代码、Spring XML、YAML、.properties和.ini文件等。基本来讲,能够实例化类和调用JavaBean兼容方法的任何配置形式都可使用。
为此,Shiro借助基于文本的INI配置提供了一个缺省的“公共”解决方案。INI易于阅读、使用简单并且需要极少依赖。你还能看到,只要简单地理解对象导航,INI可被有效地用于配置像SecurityManager那样简单的对象图。注意,Shiro还支持Spring XML配置及其他方式,但这里只我们只讨论INI。
下列清单2列出了基于INI的Shiro最简配置:
清单2. 用INI配置Shiro
在清单2中,我们看到了用于配置SecurityManager实例的INI配置例子。有两个INI段落:[main]和[users].
[main]段落是配置SecurityManager对象及其使用的其他任何对象(如Realms)的地方。在示例中,我们看到配置了两个对象:
cm对象,是Shiro的HashedCredentialsMatcher类实例。如你所见,cm实例的各属性是通过“嵌套点”语法进行配置的 - 在清单3中可以看到IniSecurityManagerFactory使用的惯例,这种方法代表了对象图导航和属性设置。
iniRealm对象,它被SecurityManager用来表示以INI格式定义的用户帐户。
[users]段落是指定用户帐户静态列表的地方 - 为简单应用或测试提供了方便。
就介绍而言,详细了解每个段落的细节并不是重点。相反,看到INI配置是一种配置Shiro的简单方式才是关键。关于INI配置的更多细节,请参见Shiro文档。
清单3. 装入shiro.ini配置文件
在清单3的示例中,我们看到有三步:
装入用来配置SecurityManager及其构成组件的INI配置文件;
根据配置创建SecurityManager实例(使用Shiro的工厂概念,它表述了工厂方法设计模式);
使应用可访问SecurityManager Singleton。在这个简单示例中,我们将它设置为VM静态Singleton,但这通常不是必须的 - 你的应用配置机制可以决定你是否需要使用静态存储。
Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。下面的清单4是通过INI配置Shiro使用LDAP目录作为应用Realm的示例。
清单4. Realm配置示例片段:连接存储用户数据的LDAP
[main]
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
ldapRealm.contextFactory.url = ldap://ldapHost:389
ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
既然已经了解如何建立一个基本的Shiro环境,下面让我们来讨论,作为一名开发者该如何使用这个框架。
认证
认证是核实用户身份的过程。也就是说,当用户使用应用进行认证时,他们就在证明他们就是自己所说的那个人。有时这也理解为“登录”。它是一个典型的三步骤过程。
收集用户的身份信息,称为当事人(principal),以及身份的支持证明,称为证书(Credential)。
将当事人和证书提交给系统。
如果提交的证书与系统期望的该用户身份(当事人)匹配,该用户就被认为是经过认证的,反之则被认为未经认证的。
这个过程的常见例子是大家都熟悉的“用户/密码”组合。多数用户在登录软件系统时,通常提供自己的用户名(当事人)和支持他们的密码(证书)。如果存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为通过认证。
Shiro以简单直观的方式支持同样的流程。正如我们前面所说,Shiro有一个以Subject为中心的API - 几乎你想要用Shiro在运行时完成的所有事情都能通过与当前执行的Subject进行交互而达成。因此,要登录Subject,只需要简单地调用它的login方法,传入表示被提交当事人和证书(在这种情况下,就是用户名和密码)的AuthenticationToken实例。示例如清单5中所示:
清单5. Subject登录
//1. 接受提交的当事人和证书:
你可以看到,Shiro的API很容易地就反映了这个常见流程。你将会在所有的Subject操作中继续看到这种简单风格。在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm,执行必须的认证检查。每个Realm都能在必要时对提交的AuthenticationTokens作出反应。但是如果登录失败了会发生什么?如果用户提供了错误密码又会发生什么?通过对Shiro的运行时AuthenticationException做出反应,你可以控制失败,参见清单6。
清单6. 控制失败的登录
//3. 登录:
你可以选择捕获AuthenticationException的一个子类,作出特定的响应,或者对任何AuthenticationException做一般性处理(例如,显示给用户普通的“错误的用户名或密码”这类消息)。选择权在你,可以根据应用需要做出选择。
Subject登录成功后,他们就被认为是已认证的,通常你会允许他们使用你的应用。但是仅仅证明了一个用户的身份并不意味着他们可以对你的应用为所欲为。这就引出了另一个问题,“我如何控制用户能做或不能做哪些事情?”,决定用户允许做哪些事情的过程被称为授权。下面我们将谈谈Shiro如何进行授权。
授权
授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web页面等等。多数用户执行访问控制是通过使用诸如角色和权限这类概念完成的。也就是说,通常用户允许或不允许做的事情是根据分配给他们的角色或权限决定的。那么,通过检查这些角色和权限,你的应用程序就可以控制哪些功能是可以暴露的。如你期望的,Subject API让你可以很容易的执行角色和权限检查。如清单7中的代码片段所示:如何检查Subject被分配了某个角色:
列表7. 角色检查
如你所见,你的应用程序可基于访问控制检查打开或关闭某些功能。
权限检查是执行授权的另一种方法。上例中的角色检查有个很大的缺陷:你无法在运行时增删角色。角色名字在这里是硬编码,所以,如果你修改了角色名字或配置,你的代码就会乱套!如果你需要在运行时改变角色含义,或想要增删角色,你必须另辟蹊径。
为此,Shiro支持了权限(permissions)概念。权限是功能的原始表述,如‘开门’,‘创建一个博文’,‘删除‘jsmith’用户’等。通过让权限反映应用的原始功能,在改变应用功能时,你只需要改变权限检查。进而,你可以在运行时按需将权限分配给角色或用户。
如清单8中,我们重写了之前的用户检查,取而代之使用权限检查。
清单8. 权限检查
这样,任何具有“user:create”权限的角色或用户都可以点击‘Create User’按钮,并且这些角色和指派甚至可以在运行时改变,这给你提供了一个非常灵活的安全模型。
“user:create”字符串是一个权限字符串的例子,它遵循特定的解析惯例。Shiro借助它的WildcardPermission支持这种开箱即用的惯例。尽管这超出了本文的范围,你会看到在创建安全策略时,WildcardPermission非常灵活,甚至支持像实例级别访问控制这样的功能。
清单9. 实例级别的权限检查
该例表明,你可以对你需要的单个资源进行访问控制,甚至深入到非常细粒度的实例级别。如果愿意,你甚至还可以发明自己的权限语法。参见Shiro Permission文档可以了解更多内容。最后,就像使用认证那样,上述调用最终会转向SecurityManager,它会咨询Realm做出自己的访问控制决定。必要时,还允许单个Realm同时响应认证和授权操作。
以上就是对Shiro授权功能的简要概述。虽然多数安全框架止于授权和认证,但Shiro提供了更多功能。下面,我们将谈谈Shiro的高级会话管理功能。
会话管理
在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。
但Shiro会话最重要的一个好处或许就是它们是独立于容器的。这具有微妙但非常强大的影响。例如,让我们考虑一下会话集群。对集群会话来讲,支持容错和故障转移有多少种容器特定的方式?Tomcat的方式与Jetty的不同,而Jetty又和Websphere不一样,等等。但通过Shiro会话,你可以获得一个容器无关的集群解决方案。Shiro的架构允许可插拔的会话数据存储,如企业缓存、关系数据库、NoSQL系统等。这意味着,只要配置会话集群一次,它就会以相同的方式工作,跟部署环境无关 - Tomcat、Jetty、JEE服务器或者独立应用。不管如何部署应用,毋须重新配置应用。
Shiro会话的另一好处就是,如果需要,会话数据可以跨客户端技术进行共享。例如,Swing桌面客户端在需要时可以参与相同的Web应用会话中 - 如果最终用户同时使用这两种应用,这样的功能会很有用。那你如何在任何环境中访问Subject的会话呢?请看下面的示例,里面使用了Subject的两个方法。
清单10. Subject的会话
如你所见,这些方法在概念上等同于HttpServletRequest API。第一个方法会返回Subject的现有会话,或者如果还没有会话,它会创建一个新的并将之返回。第二个方法接受一个布尔参数,这个参数用于判定会话不存在时是否创建新会话。一旦获得Shiro的会话,你几乎可以像使用HttpSession一样使用它。Shiro团队觉得对于Java开发者,HttpSession API用起来太舒服了,所以我们保留了它的很多感觉。当然,最大的不同在于,你可以在任何应用中使用Shiro会话,不仅限于Web应用。清单11中显示了这种相似性。
清单11. 会话的方法
加密
加密是隐藏或混淆数据以避免被偷窥的过程。在加密方面,Shiro的目标是简化并让JDK的加密支持可用。
清楚一点很重要,一般情况下,加密不是特定于Subject的,所以它是Shiro API的一部分,但并不特定于Subject。你可以在任何地方使用Shiro的加密支持,甚至在不使用Subject的情况下。对于加密支持,Shiro真正关注的两个领域是加密哈希(又名消息摘要)和加密密码。下面我们来看看这两个方面的详细描述。
哈希
如果你曾使用过JDK的MessageDigest类,你会立刻意识到它的使用有点麻烦。MessageDigest类有一个笨拙的基于工厂的静态方法API,它不是面向对象的,并且你被迫去捕获那些永远都不必捕获的Checked Exceptions。如果需要输出十六进制编码或Base64编码的消息摘要,你只有靠自己 - 对上述两种编码,没有标准的JDK支持它们。Shiro用一种干净而直观的哈希API解决了上述问题。
打个比方,考虑比较常见的情况,使用MD5哈希一个文件,并确定该哈希的十六进制值。被称为‘校验和’,这在提供文件下载时常用到 - 用户可以对下载文件执行自己的MD5哈希。如果它们匹配,用户完全可以认定文件在传输过程中没有被篡改。
不使用Shiro,你需要如下步骤才能完成上述内容:
将文件转换成字节数组。JDK中没有干这事的,故而你需要创建一个辅助方法用于打开FileInputStream,使用字节缓存区,并抛出相关的IOExceptions,等等。
使用MessageDigest类对字节数组进行哈希,处理相关异常,如清单12所示。
将哈希后的字节数组编码成十六进制字符。JDK中还是没有干这事的,你依旧需要创建另外一个辅助方法,有可能在你的实现中会使用位操作和位移动。
清单12. JDK的消息摘要
对于这样简单普遍的需求,这个工作量实在太大了。现在看看Shiro是如何做同样事情的:
当使用Shiro简化所有这些工作时,一切都非常简单明了。完成SHA-512哈希和密码的Base64编码也一样简单。
你可以看到Shiro对哈希和编码简化了不少,挽救了你处理在这类问题上所消耗的脑细胞。
密码
加密是使用密钥对数据进行可逆转换的加密算法。我们使用其保证数据的安全,尤其是传输或存储数据时,以及在数据容易被窥探的时候。
如果你曾经用过JDK的Cryptography API,特别是javax.crypto.Cipher类,你会知道它是一头需要驯服的极其复杂的野兽。对于初学者,每个可能的加密配置总是由一个javax.crypto.Cipher实例表示。必须进行公钥/私钥加密?你得用Cipher。需要为流操作使用块加密器(Block Cipher)?你得用Cipher。需要创建一个AES 256位Cipher来保护数据?你得用Cipher。你懂的。
那么如何创建你需要的Cipher实例?您得创建一个非直观、标记分隔的加密选项字符串,它被称为“转换字符串(transformation string)”,把该字符串传给Cipher.getInstance静态工厂方法。这种字符串方式的cipher选项,并没有类型安全以确保你正在用有效的选项。这也暗示没有JavaDoc帮你了解相关选项。并且,如果字符串格式组织不正确,你还需要进一步处理Checked Exception,即便你知道配置是正确的。如你所见,使用JDK Cipher是一项相当繁重的任务。很久以前,这些技术曾经是Java API的标准,但是世事变迁,我们需要一种更简单的方法。
Shiro通过引入它的CipherService API试图简化加密密码的整个概念。CipherService是多数开发者在保护数据时梦寐以求的东西:简单、无状态、线程安全的API,能够在一次方法调用中对整个数据进行加密或解密。你所需要做的只是提供你的密钥,就可根据需要加密或解密。如下列清单13中,使用256位AES加密:
清单13. Apache Shiro的加密API
较之JDK的Cipher API,Shiro的示例要简单的多:
你可以直接实例化一个CipherService - 没有奇怪或让人混乱的工厂方法;
Cipher配置选项可以表示成JavaBean - 兼容的getter和setter方法 - 没有了奇怪和难以理解的“转换字符串”;
加密和解密在单个方法调用中完成;
没有强加的Checked Exception。如果愿意,可以捕获Shiro的CryptoException。
Shiro的CipherService API还有其他好处,如同时支持基于字节数组的加密/解密(称为“块”操作)和基于流的加密/解密(如加密音频或视频)。
不必再忍受Java Cryptography带来的痛苦。Shiro的Cryptography支持就是为了减少你在确保数据安全上付出的努力。
Web支持
最后,但并非不重要,我们将简单介绍一下Shiro的Web支持。Shiro附带了一个帮助保护Web应用的强建的Web支持模块。对于Web应用,安装Shiro很简单。唯一需要做的就是在web.xml中定义一个Shiro Servlet过滤器。清单14中,列出了代码。
清单14. web.xml中的ShiroFilter
这个过滤器可以读取上述shiro.ini配置,这样不论什么开发环境,你都拥有了一致的配置体验。一旦完成配置,Shiro Filter就会过滤每个请求并且确保在请求期间特定请求的Subject是可访问的。同时由于它过滤了每个请求,你可以执行安全特定的逻辑以保证只有满足一定标准的请求才被允许通过。
URL特定的Filter链
Shiro通过其创新的URL过滤器链功能支持安全特定的过滤规则。它允许你为任何匹配的URL模式指定非正式的过滤器链。这意味着, 使用Shiro的过滤器机制,你可以很灵活的强制安全规则(或者规则的组合) - 其程度远远超过你单独在web.xml中定义过滤器时所获得的。清单15中显示了Shiro INI中的配置片段。
清单15. 路径特定的Filter链
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证。
roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
如你所见,Web应用可以使用[urls] INI段落。对于每一行,等号左边的值表示相对上下文的Web应用路径。等号右边的值定义了过滤器链 - 一个逗号分隔的有序Servlet过滤器列表,它会针对给出的路径进行执行。每个过滤器都是普通的Servlet过滤器,你看到的上面的过滤器名字(anon,user,perms,authc)是Shiro内置的安全相关的特殊过滤器。你可以搭配这些安全过滤器来创建高度定制的安全体验。你还可以指定任何其他现有的Servlet过滤器。
相比起使用web.xml,在其中先定义过滤器块,然后定义单独分离的过滤器模式块,这种方式带来的好处有多少?采用Shiro的方法,可以很容易就准确知道针对给定匹配路径执行的过滤器链。如果想这么做,你可以在web.xml中仅定义Shiro Filter,在shiro.ini中定义所有其他的过滤器和过滤器链,这要比web.xml简洁得多,而且更容易理解过滤器链定义机制。即使不使用Shiro的任何安全特性,单凭这样小小的方便之处,也值得让你使用Shiro。
JSP标签库
Shiro还提供了JSP标签库,允许你根据当前Subject的状态控制JSP页面的输出。一个有用的常见示例是在用户登录后显示“Hello <username>"文本。但若是匿名用户,你可能想要显示其他内容,如换而显示“Hello! Register Today!”。清单16显示了如何使用Shiro的JSP标签实现这个示例:
清单16. JSP 标签库示例
除了上面例子用到的标签,还有其他标签可以让你根据用户属于(或不属于)的角色,分配(或未分配)的权限,是否已认证,是否来自“记住我”服务的记忆,或是匿名访客,包含输出。
Shiro还支持其他许多Web特性,如简单的“记住我”服务,REST和BASIC认证。当然,如果想使用Shiro原生的企业会话,它还提供透明的HttpSession支持。参见Apache Shiro Web文档可以了解更多内容。
Web会话管理
最后值得一提的是Shiro在Web环境中对会话的支持。
缺省Http会话
对于Web应用,Shiro缺省将使用我们习以为常的Servlet容器会话作为其会话基础设施。即,当你调用subject.getSession()和subject.getSession(boolean)方法时,Shiro会返回Servlet容器的HttpSession实例支持的Session实例。这种方式的曼妙之处在于调用subject.getSession()的业务层代码会跟一个Shiro Session实例交互 - 还没有“认识”到它正跟一个基于Web的HttpSession打交道。这在维护架构层之间的清晰隔离时,是一件非常好的事情。
Web层中Shiro的原生会话
如果你由于需要Shiro的企业级会话特性(如容器无关的集群)而打开了Shiro的原生会话管理,你当然希望HttpServletRequest.getSession()和HttpSession API能和“原生”会话协作,而非Servlet容器会话。如果你不得不重构所有使用HttpServletRequest和HttpSession API的代码,使用Shiro的Session API来替换,这将非常令人沮丧。Shiro当然从来不会期望你这么做。相反,Shiro完整实现了Servlet规范中的Session部分以在Web应用中支持原生会话。这意味着,不管何时你使用相应的HttpServletRequest或HttpSession方法调用,Shiro都会将这些调用委托给内部的原生会话API。结果,你无需修改Web代码,即便是你正在使用Shiro的‘原生’企业会话管理 - 确实是一个非常方便(且必要)的特性。
附加特性
Apache Shiro框架还包含有对保护Java应用非常有用的其他特性,如:
为维持跨线程的Suject提供了线程和并发支持(支持Executor和ExecutorService);
为了将执行逻辑作为一种特殊的Subject,支持Callable和Runnable接口;
为了表现为另一个Subject的身份,支持“Run As”(比如,在管理应用中有用);
支持测试工具,这样可以很容易的对Shiro的安全代码进行单元测试和集成测试。
框架局限
常识告诉我们,Apache Shiro不是“银弹” - 它不能毫不费力的解决所有安全问题。如下是Shiro还未解决,但是值得知道的:
虚拟机级别的问题:Apache Shiro当前还未处理虚拟机级别的安全,比如基于访问控制策略,阻止类加载器中装入某个类。然而,Shiro集成现有的JVM安全操作并非白日做梦 - 只是没人给项目贡献这方面的工作。
多阶段认证:目前,Shiro不支持“多阶段”认证,即用户可能通过一种机制登录,当被要求再次登录时,使用另一种机制登录。这在基于Shiro的应用中已经实现,但是通过应用预先收集所有必需信息再跟Shiro交互。这个功能在Shiro的未来版本中非常有可能得到支持。
Realm写操作:目前所有Realm实现都支持“读”操作来获取验证和授权数据以执行登录和访问控制。诸如创建用户帐户、组和角色或与用户相关的角色组和权限这类“写”操作还不支持。这是因为支持这些操作的应用数据模型变化太大,很难为所有的Shiro用户强制定义“写”API。
未来的特性
Apache Shiro社区每天都在壮大,借此,Shiro的特性亦是如此。在即将发布的版本中,你可能会看到:
更干净的Web过滤机制,无需子类化就可支持更多的插件式过滤器。
更多可插拔的缺省Realm实现,优先采用组合而非继承。你可以插入查找认证和授权数据的组件,无需实现为Shiro Realm的子类;
强健的OpenID和OAuth(可能是混合)客户端支持;
支持Captcha;
针对纯无状态应用的配置简化(如,许多REST环境);
通过请求/响应协议进行多阶段认证;
通过AuthorizationRequest进行粗粒度的授权;
针对安全断言查询的ANTLR语法(比如,(‘role(admin) && (guest || !group(developer))’)
总结
Apache Shiro是一个功能齐全、健壮、通用的Java安全框架,你可以用其为你的应用护航。通过简化应用安全的四个领域,即认证、授权、会话管理和加密,在真实应用中,应用安全能更容易被理解和实现。Shiro的简单架构和兼容JavaBean使其几乎能够在任何环境下配置和使用。附加的Web支持和辅助功能,比如多线程和测试支持,让这个框架为应用安全提供了“一站式”服务。Apache Shiro开发团队将继续前进,精炼代码库和支持社区。随着持续被开源和商业应用采纳,可以预期Shiro会继续发展壮大。
Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。
记住一点,Shiro不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过相应的接口注入给Shiro即可。
Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:
1.认证 - 用户身份识别,常被称为用户“登录”;
2.授权 - 访问控制;
3.密码加密 - 保护或隐藏数据防止被偷窥;
4.会话管理 - 每个用户相关的时间敏感的状态。
Shiro还支持一些辅助特性,如Web应用安全、单元测试和多线程,它们的存在强化了上面提到的四个要素。
为何要创建Apache Shiro
对于一个框架来讲,使其有存在价值的最好例证就是有让你去用它的原因,它应该能完成一些别人无法做到的事情。要理解这一点,需要了解Shiro的历史以及创建它时的其他替代方法。
在2008年加入Apache软件基金会之前,Shiro已经5岁了,之前它被称为JSecurity项目,始于2003年初。当时,对于Java应用开发人员而言,没有太多的通用安全替代方案 - 我们被Java认证/授权服务(或称为JAAS)紧紧套牢了。JAAS有太多的缺点 - 尽管它的认证功能尚可忍受,但授权方面却显得拙劣,用起来令人沮丧。此外,JAAS跟虚拟机层面的安全问题关系非常紧密,如判断JVM中是否允许装入一个类。作为应用开发者,我更关心应用最终用户能做什么,而不是我的代码在JVM中能做什么。
由于当时正从事应用开发,我也需要一个干净、容器无关的会话机制。在当时,“这场游戏”中唯一可用的会话是HttpSessions,它需要Web容器;或是EJB 2.1里的有状态会话Bean,这又要EJB容器。而我想要的一个与容器脱钩、可用于任何我选择的环境中的会话。
最后就是加密问题。有时,我们需要保证数据安全,但是Java密码架构(Java Cryptography Architecture)让人难以理解,除非你是密码学专家。API里到处都是Checked Exception,用起来很麻烦。我需要一个干净、开箱即用的解决方案,可以在需要时方便地对数据加密/解密。
于是,纵观2003年初的安全状况,你会很快意识到还没有一个大一统的框架满足所有上述需求。有鉴于此,JSecurity(即之后的Apache Shiro)诞生了。
今天,你为何愿意使用Apache Shiro
从2003年至今,框架选择方面的情况已经改变了不少,但今天仍有令人信服的理由让你选择Shiro。其实理由相当多,Apache Shiro:
易于使用 - 易用性是这个项目的最终目标。应用安全有可能会非常让人糊涂,令人沮丧,并被认为是“必要之恶”【译注:比喻应用安全方面的编程。】。若是能让它简化到新手都能很快上手,那它将不再是一种痛苦了。
广泛性 - 没有其他安全框架可以达到Apache Shiro宣称的广度,它可以为你的安全需求提供“一站式”服务。
灵活性 - Apache Shiro可以工作在任何应用环境中。虽然它工作在Web、EJB和IoC环境中,但它并不依赖这些环境。Shiro既不强加任何规范,也无需过多依赖。
Web能力 - Apache Shiro对Web应用的支持很神奇,允许你基于应用URL和Web协议(如REST)创建灵活的安全策略,同时还提供了一套控制页面输出的JSP标签库。
可插拔 - Shiro干净的API和设计模式使它可以方便地与许多的其他框架和应用进行集成。你将看到Shiro可以与诸如Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin这类第三方框架无缝集成。
支持 - Apache Shiro是Apache软件基金会成员,这是一个公认为了社区利益最大化而行动的组织。项目开发和用户组都有随时愿意提供帮助的友善成员。像Katasoft这类商业公司,还可以给你提供需要的专业支持和服务。
谁在用Shiro
Shiro及其前身JSecurity已被各种规模和不同行业的公司项目采用多年。自从成为Apache软件基金会的顶级项目后,站点流量和使用呈持续增长态势。许多开源社区也正在用Shiro,这里有些例子如Spring,Grails,Wicket,Tapestry,Tynamo,Mule和Vaadin。
如Katasoft,Sonatype,MuleSoft这样的商业公司,一家大型社交网络和多家纽约商业银行都在使用Shiro来保护他们的商业软件和站点。
核心概念:Subject,SecurityManager和Realms
既然已经描述了Shiro的好处,那就让我们看看它的API,好让你能够有个感性认识。
Shiro架构有三个主要概念 - Subject,SecurityManager和Realms。
Subject
在考虑应用安全时,你最常问的问题可能是“当前用户是谁?”或“当前用户允许做X吗?”。当我们写代码或设计用户界面时,问自己这些问题很平常:应用通常都是基于用户故事构建的,并且你希望功能描述(和安全)是基于每个用户的。所以,对于我们而言,考虑应用安全的最自然方式就是基于当前用户。Shiro的API用它的Subject概念从根本上体现了这种思考方式。
Subject一词是一个安全术语,其基本意思是“当前的操作用户”。称之为“用户”并不准确,因为“用户”一词通常跟人相关。在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。在代码的任何地方,你都能轻易的获得Shiro Subject,参见如下代码:
清单1. 获得Subject
import org.apache.shiro.subject.Subject; import org.apache.shiro.SecurityUtils; ... Subject currentUser = SecurityUtils.getSubject();
一旦获得Subject,你就可以立即获得你希望用Shiro为当前用户做的90%的事情,如登录、登出、访问会话、执行授权检查等 - 稍后还会看到更多。这里的关键点是Shiro的API非常直观,因为它反映了开发者以‘每个用户’思考安全控制的自然趋势。同时,在代码的任何地方都能很轻松地访问Subject,允许在任何需要的地方进行安全操作。
SecurityManager
Subject的“幕后”推手是SecurityManager。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。它是Shiro框架的核心,充当“保护伞”,引用了多个内部嵌套安全组件,它们形成了对象图。但是,一旦SecurityManager及其内部对象图配置好,它就会退居幕后,应用开发人员几乎把他们的所有时间都花在Subject API调用上。
那么,如何设置SecurityManager呢?嗯,这要看应用的环境。例如,Web应用通常会在Web.xml中指定一个Shiro Servlet Filter,这会创建SecurityManager实例,如果你运行的是一个独立应用,你需要用其他配置方式,但有很多配置选项。
一个应用几乎总是只有一个SecurityManager实例。它实际是应用的Singleton(尽管不必是一个静态Singleton)。跟Shiro里的几乎所有组件一样,SecurityManager的缺省实现是POJO,而且可用POJO兼容的任何配置机制进行配置 - 普通的Java代码、Spring XML、YAML、.properties和.ini文件等。基本来讲,能够实例化类和调用JavaBean兼容方法的任何配置形式都可使用。
为此,Shiro借助基于文本的INI配置提供了一个缺省的“公共”解决方案。INI易于阅读、使用简单并且需要极少依赖。你还能看到,只要简单地理解对象导航,INI可被有效地用于配置像SecurityManager那样简单的对象图。注意,Shiro还支持Spring XML配置及其他方式,但这里只我们只讨论INI。
下列清单2列出了基于INI的Shiro最简配置:
清单2. 用INI配置Shiro
[main] cm = org.apache.shiro.authc.credential.HashedCredentialsMatcher cm.hashAlgorithm = SHA-512 cm.hashIterations = 1024 # Base64 encoding (less text): cm.storedCredentialsHexEncoded = false iniRealm.credentialsMatcher = $cm [users] jdoe = TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJpcyByZWFzb2 asmith = IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbXNoZWQsIG5vdCB
在清单2中,我们看到了用于配置SecurityManager实例的INI配置例子。有两个INI段落:[main]和[users].
[main]段落是配置SecurityManager对象及其使用的其他任何对象(如Realms)的地方。在示例中,我们看到配置了两个对象:
cm对象,是Shiro的HashedCredentialsMatcher类实例。如你所见,cm实例的各属性是通过“嵌套点”语法进行配置的 - 在清单3中可以看到IniSecurityManagerFactory使用的惯例,这种方法代表了对象图导航和属性设置。
iniRealm对象,它被SecurityManager用来表示以INI格式定义的用户帐户。
[users]段落是指定用户帐户静态列表的地方 - 为简单应用或测试提供了方便。
就介绍而言,详细了解每个段落的细节并不是重点。相反,看到INI配置是一种配置Shiro的简单方式才是关键。关于INI配置的更多细节,请参见Shiro文档。
清单3. 装入shiro.ini配置文件
import org.apache.shiro.SecurityUtils; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.util.Factory; ... //1.装入INI配置 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //2. 创建SecurityManager SecurityManager securityManager = factory.getInstance(); //3. 使其可访问 SecurityUtils.setSecurityManager(securityManager);
在清单3的示例中,我们看到有三步:
装入用来配置SecurityManager及其构成组件的INI配置文件;
根据配置创建SecurityManager实例(使用Shiro的工厂概念,它表述了工厂方法设计模式);
使应用可访问SecurityManager Singleton。在这个简单示例中,我们将它设置为VM静态Singleton,但这通常不是必须的 - 你的应用配置机制可以决定你是否需要使用静态存储。
Realm:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。下面的清单4是通过INI配置Shiro使用LDAP目录作为应用Realm的示例。
清单4. Realm配置示例片段:连接存储用户数据的LDAP
[main]
ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
ldapRealm.contextFactory.url = ldap://ldapHost:389
ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
既然已经了解如何建立一个基本的Shiro环境,下面让我们来讨论,作为一名开发者该如何使用这个框架。
认证
认证是核实用户身份的过程。也就是说,当用户使用应用进行认证时,他们就在证明他们就是自己所说的那个人。有时这也理解为“登录”。它是一个典型的三步骤过程。
收集用户的身份信息,称为当事人(principal),以及身份的支持证明,称为证书(Credential)。
将当事人和证书提交给系统。
如果提交的证书与系统期望的该用户身份(当事人)匹配,该用户就被认为是经过认证的,反之则被认为未经认证的。
这个过程的常见例子是大家都熟悉的“用户/密码”组合。多数用户在登录软件系统时,通常提供自己的用户名(当事人)和支持他们的密码(证书)。如果存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为通过认证。
Shiro以简单直观的方式支持同样的流程。正如我们前面所说,Shiro有一个以Subject为中心的API - 几乎你想要用Shiro在运行时完成的所有事情都能通过与当前执行的Subject进行交互而达成。因此,要登录Subject,只需要简单地调用它的login方法,传入表示被提交当事人和证书(在这种情况下,就是用户名和密码)的AuthenticationToken实例。示例如清单5中所示:
清单5. Subject登录
//1. 接受提交的当事人和证书:
AuthenticationToken token = new UsernamePasswordToken(username, password); //2. 获取当前Subject: Subject currentUser = SecurityUtils.getSubject(); //3. 登录: currentUser.login(token);
你可以看到,Shiro的API很容易地就反映了这个常见流程。你将会在所有的Subject操作中继续看到这种简单风格。在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm,执行必须的认证检查。每个Realm都能在必要时对提交的AuthenticationTokens作出反应。但是如果登录失败了会发生什么?如果用户提供了错误密码又会发生什么?通过对Shiro的运行时AuthenticationException做出反应,你可以控制失败,参见清单6。
清单6. 控制失败的登录
//3. 登录:
try { currentUser.login(token); } catch (IncorrectCredentialsException ice) { … } catch (LockedAccountException lae) { … } … catch (AuthenticationException ae) {… }
你可以选择捕获AuthenticationException的一个子类,作出特定的响应,或者对任何AuthenticationException做一般性处理(例如,显示给用户普通的“错误的用户名或密码”这类消息)。选择权在你,可以根据应用需要做出选择。
Subject登录成功后,他们就被认为是已认证的,通常你会允许他们使用你的应用。但是仅仅证明了一个用户的身份并不意味着他们可以对你的应用为所欲为。这就引出了另一个问题,“我如何控制用户能做或不能做哪些事情?”,决定用户允许做哪些事情的过程被称为授权。下面我们将谈谈Shiro如何进行授权。
授权
授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web页面等等。多数用户执行访问控制是通过使用诸如角色和权限这类概念完成的。也就是说,通常用户允许或不允许做的事情是根据分配给他们的角色或权限决定的。那么,通过检查这些角色和权限,你的应用程序就可以控制哪些功能是可以暴露的。如你期望的,Subject API让你可以很容易的执行角色和权限检查。如清单7中的代码片段所示:如何检查Subject被分配了某个角色:
列表7. 角色检查
if ( subject.hasRole(“administrator”) ) { //显示‘Create User’按钮 } else { //按钮置灰? }
如你所见,你的应用程序可基于访问控制检查打开或关闭某些功能。
权限检查是执行授权的另一种方法。上例中的角色检查有个很大的缺陷:你无法在运行时增删角色。角色名字在这里是硬编码,所以,如果你修改了角色名字或配置,你的代码就会乱套!如果你需要在运行时改变角色含义,或想要增删角色,你必须另辟蹊径。
为此,Shiro支持了权限(permissions)概念。权限是功能的原始表述,如‘开门’,‘创建一个博文’,‘删除‘jsmith’用户’等。通过让权限反映应用的原始功能,在改变应用功能时,你只需要改变权限检查。进而,你可以在运行时按需将权限分配给角色或用户。
如清单8中,我们重写了之前的用户检查,取而代之使用权限检查。
清单8. 权限检查
if ( subject.isPermitted(“user:create”) ) { //显示‘Create User’按钮 } else { //按钮置灰? }
这样,任何具有“user:create”权限的角色或用户都可以点击‘Create User’按钮,并且这些角色和指派甚至可以在运行时改变,这给你提供了一个非常灵活的安全模型。
“user:create”字符串是一个权限字符串的例子,它遵循特定的解析惯例。Shiro借助它的WildcardPermission支持这种开箱即用的惯例。尽管这超出了本文的范围,你会看到在创建安全策略时,WildcardPermission非常灵活,甚至支持像实例级别访问控制这样的功能。
清单9. 实例级别的权限检查
if ( subject.isPermitted(“user:delete:jsmith”) ) { //删除‘jsmith’用户 } else { //不删除‘jsmith’ }
该例表明,你可以对你需要的单个资源进行访问控制,甚至深入到非常细粒度的实例级别。如果愿意,你甚至还可以发明自己的权限语法。参见Shiro Permission文档可以了解更多内容。最后,就像使用认证那样,上述调用最终会转向SecurityManager,它会咨询Realm做出自己的访问控制决定。必要时,还允许单个Realm同时响应认证和授权操作。
以上就是对Shiro授权功能的简要概述。虽然多数安全框架止于授权和认证,但Shiro提供了更多功能。下面,我们将谈谈Shiro的高级会话管理功能。
会话管理
在安全框架领域,Apache Shiro提供了一些独特的东西:可在任何应用或架构层一致地使用Session API。即,Shiro为任何应用提供了一个会话编程范式 - 从小型后台独立应用到大型集群Web应用。这意味着,那些希望使用会话的应用开发者,不必被迫使用Servlet或EJB容器了。或者,如果正在使用这些容器,开发者现在也可以选择使用在任何层统一一致的会话API,取代Servlet或EJB机制。
但Shiro会话最重要的一个好处或许就是它们是独立于容器的。这具有微妙但非常强大的影响。例如,让我们考虑一下会话集群。对集群会话来讲,支持容错和故障转移有多少种容器特定的方式?Tomcat的方式与Jetty的不同,而Jetty又和Websphere不一样,等等。但通过Shiro会话,你可以获得一个容器无关的集群解决方案。Shiro的架构允许可插拔的会话数据存储,如企业缓存、关系数据库、NoSQL系统等。这意味着,只要配置会话集群一次,它就会以相同的方式工作,跟部署环境无关 - Tomcat、Jetty、JEE服务器或者独立应用。不管如何部署应用,毋须重新配置应用。
Shiro会话的另一好处就是,如果需要,会话数据可以跨客户端技术进行共享。例如,Swing桌面客户端在需要时可以参与相同的Web应用会话中 - 如果最终用户同时使用这两种应用,这样的功能会很有用。那你如何在任何环境中访问Subject的会话呢?请看下面的示例,里面使用了Subject的两个方法。
清单10. Subject的会话
Session session = subject.getSession(); Session session = subject.getSession(boolean create);
如你所见,这些方法在概念上等同于HttpServletRequest API。第一个方法会返回Subject的现有会话,或者如果还没有会话,它会创建一个新的并将之返回。第二个方法接受一个布尔参数,这个参数用于判定会话不存在时是否创建新会话。一旦获得Shiro的会话,你几乎可以像使用HttpSession一样使用它。Shiro团队觉得对于Java开发者,HttpSession API用起来太舒服了,所以我们保留了它的很多感觉。当然,最大的不同在于,你可以在任何应用中使用Shiro会话,不仅限于Web应用。清单11中显示了这种相似性。
清单11. 会话的方法
Session session = subject.getSession(); session.getAttribute("key", someValue); Date start = session.getStartTimestamp(); Date timestamp = session.getLastAccessTime(); session.setTimeout(millis); ...
加密
加密是隐藏或混淆数据以避免被偷窥的过程。在加密方面,Shiro的目标是简化并让JDK的加密支持可用。
清楚一点很重要,一般情况下,加密不是特定于Subject的,所以它是Shiro API的一部分,但并不特定于Subject。你可以在任何地方使用Shiro的加密支持,甚至在不使用Subject的情况下。对于加密支持,Shiro真正关注的两个领域是加密哈希(又名消息摘要)和加密密码。下面我们来看看这两个方面的详细描述。
哈希
如果你曾使用过JDK的MessageDigest类,你会立刻意识到它的使用有点麻烦。MessageDigest类有一个笨拙的基于工厂的静态方法API,它不是面向对象的,并且你被迫去捕获那些永远都不必捕获的Checked Exceptions。如果需要输出十六进制编码或Base64编码的消息摘要,你只有靠自己 - 对上述两种编码,没有标准的JDK支持它们。Shiro用一种干净而直观的哈希API解决了上述问题。
打个比方,考虑比较常见的情况,使用MD5哈希一个文件,并确定该哈希的十六进制值。被称为‘校验和’,这在提供文件下载时常用到 - 用户可以对下载文件执行自己的MD5哈希。如果它们匹配,用户完全可以认定文件在传输过程中没有被篡改。
不使用Shiro,你需要如下步骤才能完成上述内容:
将文件转换成字节数组。JDK中没有干这事的,故而你需要创建一个辅助方法用于打开FileInputStream,使用字节缓存区,并抛出相关的IOExceptions,等等。
使用MessageDigest类对字节数组进行哈希,处理相关异常,如清单12所示。
将哈希后的字节数组编码成十六进制字符。JDK中还是没有干这事的,你依旧需要创建另外一个辅助方法,有可能在你的实现中会使用位操作和位移动。
清单12. JDK的消息摘要
try { MessageDigest md = MessageDigest.getInstance("MD5"); md.digest(bytes); byte[] hashed = md.digest(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
对于这样简单普遍的需求,这个工作量实在太大了。现在看看Shiro是如何做同样事情的:
String hex = new Md5Hash(myFile).toHex();
当使用Shiro简化所有这些工作时,一切都非常简单明了。完成SHA-512哈希和密码的Base64编码也一样简单。
String encodedPassword = new Sha512Hash(password, salt, count).toBase64();
你可以看到Shiro对哈希和编码简化了不少,挽救了你处理在这类问题上所消耗的脑细胞。
密码
加密是使用密钥对数据进行可逆转换的加密算法。我们使用其保证数据的安全,尤其是传输或存储数据时,以及在数据容易被窥探的时候。
如果你曾经用过JDK的Cryptography API,特别是javax.crypto.Cipher类,你会知道它是一头需要驯服的极其复杂的野兽。对于初学者,每个可能的加密配置总是由一个javax.crypto.Cipher实例表示。必须进行公钥/私钥加密?你得用Cipher。需要为流操作使用块加密器(Block Cipher)?你得用Cipher。需要创建一个AES 256位Cipher来保护数据?你得用Cipher。你懂的。
那么如何创建你需要的Cipher实例?您得创建一个非直观、标记分隔的加密选项字符串,它被称为“转换字符串(transformation string)”,把该字符串传给Cipher.getInstance静态工厂方法。这种字符串方式的cipher选项,并没有类型安全以确保你正在用有效的选项。这也暗示没有JavaDoc帮你了解相关选项。并且,如果字符串格式组织不正确,你还需要进一步处理Checked Exception,即便你知道配置是正确的。如你所见,使用JDK Cipher是一项相当繁重的任务。很久以前,这些技术曾经是Java API的标准,但是世事变迁,我们需要一种更简单的方法。
Shiro通过引入它的CipherService API试图简化加密密码的整个概念。CipherService是多数开发者在保护数据时梦寐以求的东西:简单、无状态、线程安全的API,能够在一次方法调用中对整个数据进行加密或解密。你所需要做的只是提供你的密钥,就可根据需要加密或解密。如下列清单13中,使用256位AES加密:
清单13. Apache Shiro的加密API
AesCipherService cipherService = new AesCipherService(); cipherService.setKeySize(256); //创建一个测试密钥: byte[] testKey = cipherService.generateNewKey(); //加密文件的字节: byte[] encrypted = cipherService.encrypt(fileBytes, testKey);
较之JDK的Cipher API,Shiro的示例要简单的多:
你可以直接实例化一个CipherService - 没有奇怪或让人混乱的工厂方法;
Cipher配置选项可以表示成JavaBean - 兼容的getter和setter方法 - 没有了奇怪和难以理解的“转换字符串”;
加密和解密在单个方法调用中完成;
没有强加的Checked Exception。如果愿意,可以捕获Shiro的CryptoException。
Shiro的CipherService API还有其他好处,如同时支持基于字节数组的加密/解密(称为“块”操作)和基于流的加密/解密(如加密音频或视频)。
不必再忍受Java Cryptography带来的痛苦。Shiro的Cryptography支持就是为了减少你在确保数据安全上付出的努力。
Web支持
最后,但并非不重要,我们将简单介绍一下Shiro的Web支持。Shiro附带了一个帮助保护Web应用的强建的Web支持模块。对于Web应用,安装Shiro很简单。唯一需要做的就是在web.xml中定义一个Shiro Servlet过滤器。清单14中,列出了代码。
清单14. web.xml中的ShiroFilter
<filter> <filter-name>ShiroFilter</filter-name> <filter-class> org.apache.shiro.web.servlet.IniShiroFilter </filter-class> <!-- 没有init-param属性就表示从classpath:shiro.ini装入INI配置 --> </filter> <filter-mapping> <filter-name>ShiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
这个过滤器可以读取上述shiro.ini配置,这样不论什么开发环境,你都拥有了一致的配置体验。一旦完成配置,Shiro Filter就会过滤每个请求并且确保在请求期间特定请求的Subject是可访问的。同时由于它过滤了每个请求,你可以执行安全特定的逻辑以保证只有满足一定标准的请求才被允许通过。
URL特定的Filter链
Shiro通过其创新的URL过滤器链功能支持安全特定的过滤规则。它允许你为任何匹配的URL模式指定非正式的过滤器链。这意味着, 使用Shiro的过滤器机制,你可以很灵活的强制安全规则(或者规则的组合) - 其程度远远超过你单独在web.xml中定义过滤器时所获得的。清单15中显示了Shiro INI中的配置片段。
清单15. 路径特定的Filter链
[urls] /assets/** = anon /user/signup = anon /user/** = user /rpc/rest/** = perms[rpc:invoke], authc /** = authc
anon org.apache.shiro.web.filter.authc.AnonymousFilter authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter logout org.apache.shiro.web.filter.authc.LogoutFilter noSessionCreation org.apache.shiro.web.filter.session.NoSessionCreationFilter perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter port org.apache.shiro.web.filter.authz.PortFilter rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter ssl org.apache.shiro.web.filter.authz.SslFilter user org.apache.shiro.web.filter.authc.UserFilter
anon:例子/admins/**=anon 没有参数,表示可以匿名使用。
authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证。
roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。
ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
如你所见,Web应用可以使用[urls] INI段落。对于每一行,等号左边的值表示相对上下文的Web应用路径。等号右边的值定义了过滤器链 - 一个逗号分隔的有序Servlet过滤器列表,它会针对给出的路径进行执行。每个过滤器都是普通的Servlet过滤器,你看到的上面的过滤器名字(anon,user,perms,authc)是Shiro内置的安全相关的特殊过滤器。你可以搭配这些安全过滤器来创建高度定制的安全体验。你还可以指定任何其他现有的Servlet过滤器。
相比起使用web.xml,在其中先定义过滤器块,然后定义单独分离的过滤器模式块,这种方式带来的好处有多少?采用Shiro的方法,可以很容易就准确知道针对给定匹配路径执行的过滤器链。如果想这么做,你可以在web.xml中仅定义Shiro Filter,在shiro.ini中定义所有其他的过滤器和过滤器链,这要比web.xml简洁得多,而且更容易理解过滤器链定义机制。即使不使用Shiro的任何安全特性,单凭这样小小的方便之处,也值得让你使用Shiro。
JSP标签库
Shiro还提供了JSP标签库,允许你根据当前Subject的状态控制JSP页面的输出。一个有用的常见示例是在用户登录后显示“Hello <username>"文本。但若是匿名用户,你可能想要显示其他内容,如换而显示“Hello! Register Today!”。清单16显示了如何使用Shiro的JSP标签实现这个示例:
清单16. JSP 标签库示例
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> ... <p>Hello <shiro:user> <!-- shiro:principal打印出了Subject的主当事人 - 在这个示例中,就是用户名: --> <shiro:principal/>! </shiro:user> <shiro:guest> <!-- 没有登录 - 就认为是Guest。显示注册链接: --> ! <a href=”register.jsp”>Register today!</a> </shiro:guest> </p>
除了上面例子用到的标签,还有其他标签可以让你根据用户属于(或不属于)的角色,分配(或未分配)的权限,是否已认证,是否来自“记住我”服务的记忆,或是匿名访客,包含输出。
Shiro还支持其他许多Web特性,如简单的“记住我”服务,REST和BASIC认证。当然,如果想使用Shiro原生的企业会话,它还提供透明的HttpSession支持。参见Apache Shiro Web文档可以了解更多内容。
Web会话管理
最后值得一提的是Shiro在Web环境中对会话的支持。
缺省Http会话
对于Web应用,Shiro缺省将使用我们习以为常的Servlet容器会话作为其会话基础设施。即,当你调用subject.getSession()和subject.getSession(boolean)方法时,Shiro会返回Servlet容器的HttpSession实例支持的Session实例。这种方式的曼妙之处在于调用subject.getSession()的业务层代码会跟一个Shiro Session实例交互 - 还没有“认识”到它正跟一个基于Web的HttpSession打交道。这在维护架构层之间的清晰隔离时,是一件非常好的事情。
Web层中Shiro的原生会话
如果你由于需要Shiro的企业级会话特性(如容器无关的集群)而打开了Shiro的原生会话管理,你当然希望HttpServletRequest.getSession()和HttpSession API能和“原生”会话协作,而非Servlet容器会话。如果你不得不重构所有使用HttpServletRequest和HttpSession API的代码,使用Shiro的Session API来替换,这将非常令人沮丧。Shiro当然从来不会期望你这么做。相反,Shiro完整实现了Servlet规范中的Session部分以在Web应用中支持原生会话。这意味着,不管何时你使用相应的HttpServletRequest或HttpSession方法调用,Shiro都会将这些调用委托给内部的原生会话API。结果,你无需修改Web代码,即便是你正在使用Shiro的‘原生’企业会话管理 - 确实是一个非常方便(且必要)的特性。
附加特性
Apache Shiro框架还包含有对保护Java应用非常有用的其他特性,如:
为维持跨线程的Suject提供了线程和并发支持(支持Executor和ExecutorService);
为了将执行逻辑作为一种特殊的Subject,支持Callable和Runnable接口;
为了表现为另一个Subject的身份,支持“Run As”(比如,在管理应用中有用);
支持测试工具,这样可以很容易的对Shiro的安全代码进行单元测试和集成测试。
框架局限
常识告诉我们,Apache Shiro不是“银弹” - 它不能毫不费力的解决所有安全问题。如下是Shiro还未解决,但是值得知道的:
虚拟机级别的问题:Apache Shiro当前还未处理虚拟机级别的安全,比如基于访问控制策略,阻止类加载器中装入某个类。然而,Shiro集成现有的JVM安全操作并非白日做梦 - 只是没人给项目贡献这方面的工作。
多阶段认证:目前,Shiro不支持“多阶段”认证,即用户可能通过一种机制登录,当被要求再次登录时,使用另一种机制登录。这在基于Shiro的应用中已经实现,但是通过应用预先收集所有必需信息再跟Shiro交互。这个功能在Shiro的未来版本中非常有可能得到支持。
Realm写操作:目前所有Realm实现都支持“读”操作来获取验证和授权数据以执行登录和访问控制。诸如创建用户帐户、组和角色或与用户相关的角色组和权限这类“写”操作还不支持。这是因为支持这些操作的应用数据模型变化太大,很难为所有的Shiro用户强制定义“写”API。
未来的特性
Apache Shiro社区每天都在壮大,借此,Shiro的特性亦是如此。在即将发布的版本中,你可能会看到:
更干净的Web过滤机制,无需子类化就可支持更多的插件式过滤器。
更多可插拔的缺省Realm实现,优先采用组合而非继承。你可以插入查找认证和授权数据的组件,无需实现为Shiro Realm的子类;
强健的OpenID和OAuth(可能是混合)客户端支持;
支持Captcha;
针对纯无状态应用的配置简化(如,许多REST环境);
通过请求/响应协议进行多阶段认证;
通过AuthorizationRequest进行粗粒度的授权;
针对安全断言查询的ANTLR语法(比如,(‘role(admin) && (guest || !group(developer))’)
总结
Apache Shiro是一个功能齐全、健壮、通用的Java安全框架,你可以用其为你的应用护航。通过简化应用安全的四个领域,即认证、授权、会话管理和加密,在真实应用中,应用安全能更容易被理解和实现。Shiro的简单架构和兼容JavaBean使其几乎能够在任何环境下配置和使用。附加的Web支持和辅助功能,比如多线程和测试支持,让这个框架为应用安全提供了“一站式”服务。Apache Shiro开发团队将继续前进,精炼代码库和支持社区。随着持续被开源和商业应用采纳,可以预期Shiro会继续发展壮大。
发表评论
文章已被作者锁定,不允许评论。
-
Spring BeanFactoryPostProcessor和BeanPostProcessor的区别
2018-11-14 15:40 706链接:https://blog.csdn.net/caihai ... -
spring BeanPostProcessor理解
2018-11-14 11:31 321链接:https://blog.csdn.net/ginkgo ... -
Spring 源码解析之Initializer
2018-11-14 11:27 456链接:https://blog.csdn.net/ktlife ... -
spring transaction同一个类不回滚解决方法
2018-10-11 10:59 7711.修改配置文件 <aop:aspectj-autopr ... -
Spring @Transaction学习
2018-10-08 10:36 2921.考虑有下面这么一个类 public class Foo ... -
spring mvc i18n国际化学习(spring:message)
2018-06-09 09:35 641spring.xml文件中配置: <!-- 存储区域 ... -
Spring Boot Oauth2.0授权服务器
2018-05-11 14:19 1652什么是OAuth? OAuth(Open Authoriza ... -
Spring Boot @Import注解(将指定类实例注入到IOC容器中)
2018-05-09 10:20 1598SpringBoot 的 @Import 用于将指定的类实例注 ... -
Spring Boot @Conditional注解
2018-05-09 10:15 1817Spring Boot的强大之处在于使用了Spring 4框架 ... -
Spring Boot自定义starter pom实例(/META-INFO/spring.factory文件)
2018-05-09 09:48 1140自定义starter pom 自己实现一个简单的例子,当某个类 ... -
Spring Boot自动配置原理(@Conditional @Import)
2018-04-26 14:45 1339Springboot的自动配置是SpringBoot的关键,主 ... -
Spring Boot优缺点总结
2018-04-16 10:25 1537优点: 1.去除了大量的xml配置文件 2.简化 ... -
SpringBoot JPA @Transaction 知识学习
2018-03-16 09:09 762一、事务相关概念 1、事务的特点 原子性:事务是一个原子操 ... -
Sprint @Query注解的用法(nativeQuery=true/false)(Spring Data JPA)
2018-03-15 16:33 38451. 一个使用@Query注解的简单例子 @Query(val ... -
Spring Boot JpaRepository知识学习(Spring Data JPA)
2018-03-14 11:17 18031.Spring Data所解决的问题 Spring Dat ... -
SpringCloud Hystrix知识学习(防止雪崩效应)
2018-03-13 14:57 930一、Hystrix说明 1.服务雪崩效应:是一种因服务提供者的 ... -
SpringCloud @LoadBalanced注解学习
2018-03-13 09:48 2221当时我们说开启负载均衡很简单,只需要在RestTemplate ... -
Spring Boot配置方式(java配置和注解配置)
2018-03-12 15:09 1111Java配置 从Spring 3.x开始,Spring提供了J ... -
java RestTemplate访问restful服务
2018-03-01 15:02 1622REST的基础知识 当谈论REST时,有一种常见的错误就是将其 ... -
SpringCloud | 第七篇: 高可用的服务注册中心
2018-02-26 14:31 485文章 第一篇: 服务的注册与发现(Eureka) 介绍了服务注 ...
相关推荐
### Apache Shiro:全方位守护你的应用安全 #### 引言 在当今数字化时代,信息安全已成为企业...通过深入理解并正确运用Shiro的核心功能,开发者可以有效地保护应用免受各种安全威胁,为用户提供更加安全的使用体验。
总的来说,Apache Shiro提供了一套强大且易于使用的工具,帮助开发者快速构建安全的应用程序。无论是小型的命令行工具,还是大型的企业级Web应用,Shiro都能提供相应的安全防护。由于其灵活性和简洁性,越来越多的...
Apache Shiro是一个强大的Java安全框架,它为应用程序提供了身份验证(Authentication)、授权(Authorization)、会话管理(Session Management)和加密(Cryptography)等核心功能。在这个名为"Shiro应用实例1"的...
通过这个项目,你将能够深入理解Shiro的加密机制,掌握如何在实际应用中使用Shiro来保护敏感数据,提升应用的安全性。同时,Eclipse的开发环境也会使这个过程更加直观和便捷。在学习过程中,还可以参考Shiro的官方...
本教程将深入探讨如何使用Shiro来保护Web应用程序,确保只有经过验证和授权的用户能够访问敏感资源。 首先,我们需要理解Shiro的核心概念: 1. 身份验证(Authentication):这是验证用户身份的过程,即确认用户是...
**初识Shiro应用**:Apache Shiro 教程提供了一个简单的入门示例,指导开发者创建首个受Shiro保护的应用。通过这个教程,你可以了解到Shiro的基本概念和API,为后续更深入的学习打下基础。 总结来说,Apache Shiro ...
Shiro 可以广泛应用于各种类型的 Java 应用程序中,无论是 Web 应用、桌面应用还是服务端应用。其灵活性使得开发者可以根据项目的具体需求选择适当的组件进行集成。 - **Web 应用**:Shiro 可以为 Web 应用提供强大...
5. Web支持(Web Support):Shiro提供了一套API来帮助保护Web应用程序,例如过滤器和相关的Web安全机制。 6. 缓存(Caching):缓存机制是Shiro的核心特性之一,用来提高安全操作的性能和效率。 7. 并发...
- Web支持(Web Support):提供API以帮助保护Web应用程序。 - 缓存(Caching):缓存是Shiro中的重要组成部分,用以保证安全操作的快速高效。 - 并发支持(Concurrency):Shiro利用其并发特性支持多线程应用程序...
Apache Shiro是一个强大的Java安全框架,它提供了身份验证、授权、加密和会话管理功能,为开发人员构建安全的应用程序...通过深入学习和实践,你可以掌握如何利用Shiro提供的工具和机制,创建安全、健壮的应用程序。
你可以从中学习到如何配置Shiro的realm(用于连接应用程序与安全数据源),以及如何设置权限控制,例如基于角色的访问控制(RBAC)和自定义权限表达式。 "shiro入门学习.ppt"是Shiro的基础教程,可能包含了Shiro的...
常见的实现方式包括JDBCRealm,适用于使用关系数据库的应用程序。此外还有CASRealm等其他类型的Realm,用于不同的应用场景。 #### 五、为何选择Shiro 尽管Spring Security也是一个非常强大的安全框架,但Shiro在...
Shiro的目的是为了保护应用程序和系统,从简单的命令行应用程序到复杂的网络和企业应用程序都可以使用。 Shiro的安全体系结构由三个主要的概念组成:Subject(当前操作的用户)、SecurityManager(Shiro架构的核心...
3. **shiro-guice-1.7.1.jar**: Guice是Google提供的一个轻量级依赖注入框架,Shiro-Guice模块允许你在Guice管理的应用程序中使用Shiro。这使得Shiro的组件可以通过Guice的依赖注入机制进行初始化和管理。 4. **...
4. **自定义 Realm**:Shiro通过Realm与应用程序的数据源交互,获取用户信息。如果包含自定义Realm,那可能是为了连接特定数据库或其他数据源进行用户验证。 学习和使用Shiro,你可以了解如何创建安全的登录系统,...
Apache Shiro是一个强大的Java安全框架,它为开发者提供了认证、授权、加密和会话管理等功能,使得在构建...通过理解Shiro的基本概念和组件,开发者能够更有效地利用Shiro保护他们的应用程序,并确保用户数据的安全。
4. **会话管理(Session Management)**:Shiro不仅可以管理应用程序中的会话,还可以跨多个应用服务器实现分布式会话管理。 在手册中,你可以找到以下内容的详细解释: 1. **快速入门**:介绍如何在项目中引入...
Apache Shiro是一个强大且易用的Java安全框架,它提供了认证、授权、加密和会话管理功能,可以非常轻松地开发出足够安全的应用程序。在"Shiro_lib.zip"这个压缩包中,我们很显然看到它包含了一系列与Apache Shiro...
开发者在升级时应遵循最佳实践,确保无缝过渡,并利用Shiro的强大功能来保护他们的应用程序。此外,了解并正确配置Shiro的各个组件,如Realms(用于认证和授权的数据源)、Filters(处理HTTP请求的过滤器)和...
Shiro 是一种功能强大且易于使用的 Java 安全框架,它执行身份验证、授权、加密和会话管理, 可用于保护从命令行应用程序、移动应用程序到 Web 和企业应用程序等应用的安全。Shiro 的核心组件包括 Subject、...