让Apache Shiro保护你的应用[转]

尝试保护你的应用时,你是否有过挫败感?是否以为现有的Java安全解决方案难以使用,只会让你更糊涂?本文介绍的Apache Shiro,是一个不一样寻常的Java安全框架,为保护应用提供了简单而强大的方法。本文还解释了Apache Shiro的项目目标、架构理念以及如何使用Shiro为应用安全保驾护航。html

什么是Apache Shiro?

Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、受权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。java

Shiro为解决下列问题(我喜欢称它们为应用安全的四要素)提供了保护应用的API:web

  • 认证 - 用户身份识别,常被称为用户“登陆”;
  • 受权 - 访问控制;
  • 密码加密 - 保护或隐藏数据防止被偷窥;
  • 会话管理 - 每用户相关的时间敏感的状态。

Shiro还支持一些辅助特性,如Web应用安全、单元测试和多线程,它们的存在强化了上面提到的四个要素。算法

为什么要建立Apache Shiro?

对于一个框架来说,使其有存在价值的最好例证就是有让你去用它的缘由,它应该能完成一些别人没法作到的事情。要理解这一点,须要了解Shiro的历史以及建立它时的其余替代方法。数据库

在2008年加入Apache软件基金会以前,Shiro已经5岁了,以前它被称为JSecurity项目,始于2003年初。当时,对于Java应用开发人员而言,没有太多的通用安全替代方案 - 咱们被Java认证/受权服务(或称为JAAS)牢牢套牢了。JAAS有太多的缺点 - 尽管它的认证功能尚可忍受,但受权方面却显得拙劣,用起来使人沮丧。此外,JAAS跟虚拟机层面的安全问题关系很是紧密,如判断JVM中是否容许装入一个类。做为应用开发者,我更关心应用最终用户能作什么,而不是个人代码在JVM中能作什么。apache

因为当时正从事应用开发,我也须要一个干净、容器无关的会话机制。在当时,“这场游戏”中惟一可用的会话是HttpSessions,它须要Web容器;或是EJB 2.1里的有状态会话Bean,这又要EJB容器。而我想要的一个与容器脱钩、可用于任何我选择的环境中的会话。编程

最后就是加密问题。有时,咱们须要保证数据安全,可是Java密码架构(Java Cryptography Architecture)让人难以理解,除非你是密码学专家。API里处处都是Checked Exception,用起来很麻烦。我须要一个干净、开箱即用的解决方案,能够在须要时方便地对数据加密/解密。设计模式

因而,纵观2003年初的安全情况,你会很快意识到尚未一个大一统的框架知足全部上述需求。有鉴于此,JSecurity(即以后的Apache Shiro)诞生了。api

今天,你为什么愿意使用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)的地方。在示例中,咱们看到配置了两个对象:

  1. cm对象,是Shiro的HashedCredentialsMatcher类实例。如你所见,cm实例的各属性是经过“嵌套点”语法进行配置的 - 在清单3中能够看到IniSecurityManagerFactory使用的惯例,这种方法表明了对象图导航和属性设置。
  2. 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的示例中,咱们看到有三步:

  1. 装入用来配置SecurityManager及其构成组件的INI配置文件;
  2. 根据配置建立SecurityManager实例(使用Shiro的工厂概念,它表述了工厂方法设计模式);
  3. 使应用可访问SecurityManager Singleton。在这个简单示例中,咱们将它设置为VM静态Singleton,但这一般不是必须的 - 你的应用配置机制能够决定你是否须要使用静态存储。

Realms

Shiro的第三个也是最后一个概念是Realm。Realm充当了Shiro与应用安全数据间的“桥梁”或者“链接器”。也就是说,当切实与像用户账户这类安全相关数据进行交互,执行认证(登陆)和受权(访问控制)时,Shiro会从应用配置的Realm中查找不少内容。

从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的链接细节,并在须要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)受权。配置多个Realm是能够的,可是至少须要一个。

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环境,下面让咱们来讨论,做为一名开发者该如何使用这个框架。

认证

认证是核实用户身份的过程。也就是说,当用户使用应用进行认证时,他们就在证实他们就是本身所说的那我的。有时这也理解为“登陆”。它是一个典型的三步骤过程。

  1. 收集用户的身份信息,称为当事人(principal),以及身份的支持证实,称为证书(Credential)
  2. 将当事人和证书提交给系统。
  3. 若是提交的证书与系统指望的该用户身份(当事人)匹配,该用户就被认为是通过认证的,反之则被认为未经认证的。

这个过程的常见例子是你们都熟悉的“用户/密码”组合。多数用户在登陆软件系统时,一般提供本身的用户名(当事人)和支持他们的密码(证书)。若是存储在系统中的密码(或密码表示)与用户提供的匹配,他们就被认为经过认证。

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,你须要以下步骤才能完成上述内容:

  1. 将文件转换成字节数组。JDK中没有干这事的,故而你须要建立一个辅助方法用于打开FileInputStream,使用字节缓存区,并抛出相关的IOExceptions,等等。
  2. 使用MessageDigest类对字节数组进行哈希,处理相关异常,如清单12所示。
  3. 将哈希后的字节数组编码成十六进制字符。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

如你所见,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的子类;
  • 强健的OpenIDOAuth(多是混合)客户端支持;
  • 支持Captcha;
  • 针对纯无状态应用的配置简化(如,许多REST环境);
  • 经过请求/响应协议进行多阶段认证;
  • 经过AuthorizationRequest进行粗粒度的受权;
  • 针对安全断言查询的ANTLR语法(好比,(‘role(admin) && (guest || !group(developer))’)

总结

Apache Shiro是一个功能齐全、健壮、通用的Java安全框架,你能够用其为你的应用护航。经过简化应用安全的四个领域,即认证、受权、会话管理和加密,在真实应用中,应用安全能更容易被理解和实现。Shiro的简单架构和兼容JavaBean使其几乎可以在任何环境下配置和使用。附加的Web支持和辅助功能,好比多线程和测试支持,让这个框架为应用安全提供了“一站式”服务。Apache Shiro开发团队将继续前进,精炼代码库和支持社区。随着持续被开源和商业应用采纳,能够预期Shiro会继续发展壮大。

资源

关于做者

Les Hazlewood是Apache Shiro PMC主席以及Katasoft的CTO和合伙创办人,该公司是专一于应用安全产品以及提供Apache Shiro专业支持的创业公司。做为专业Java开发人员及企业架构师,以及以前Bloomberg,Delta Airlines和JBoss的高级角色,Les拥有10年的的经验。Les积极从事开源开发已有9年时间,提交或作出贡献的项目有Spring框架、Hibernate、JBoss,OpenSpaces,固然还有JSecurity,Apache Shiro的前身。Les目前居住在加州的圣马特奥,不编程时,他会练习剑道和学习日语。

查看英文原文: Application Security With Apache Shiro

相关文章
相关标签/搜索