以前在工做中有比较快速的学习过Shiro安全框架,如今因为新项目须要,深刻研究和学习Shiro的一些知识,填补安全管理方面的知识欠缺,使咱们在web 开发领域更具竞争力,不作只会CRUD的程序员!html
Shiro是一个Java安全框架,执行身份验证、受权、密码、会话管理。Shiro是Apache 的一个开源项目,前身是JSecurity 项目,始于2003年初。程序员
Shiro 能够为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。web
shiro 解决了应用安全的四要素:算法
同时,Shiro另外支持了一些辅助特性:如 Web 应用安全、单元测试和多线程,它们的存在强化了上面提到的四个要素。数据库
从 2003 年至今,框架选择方面的状况已经改变了很多,但今天仍有使人信服的理由让你选择 Shiro。其实理由至关多,Apache Shiro:apache
一、易于使用 - 易用性是这个项目的最终目标。应用安全有可能会很是让人糊涂,使人沮丧。如果能让它简化到新手都能很快上手,那它将再也不是一种痛苦了。编程
二、普遍性 - 没有其余安全框架能够达到 Apache Shiro 宣称的广度,它能够为你的安全需求提供“一站式”服务。设计模式
三、灵活性 - Apache Shiro 能够工做在任何应用环境中。虽然它工做在 Web、EJB 和 IoC 环境中,但它并不依赖这些环境。Shiro 既不强加任何规范,也无需过多依赖。api
四、Web 能力 - Apache Shiro 对 Web 应用的支持很神奇,容许你基于应用 URL 和 Web 协议(如 REST)建立灵活的安全策略,同时还提供了一套控制页面输出的 JSP 标签库。数组
五、可插拔 - Shiro 干净的 API 和设计模式使它能够方便地与许多的其余框架和应用进行集成。你将看到 Shiro 能够与诸如 Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin 这类第三方框架无缝集成。
六、支持 - Apache Shiro 是 Apache 软件基金会成员,这是一个公认为了社区利益最大化而行动的组织。项目开发和用户组都有随时愿意提供帮助的友善成员。
Shiro的核心概念有三个:Subject,SecurityManager 和 Realms。
subject 被Shiro 描述为一个主体,对于web应用来讲,能够简单理解为用户。
这里咱们来阐述一个Shiro设计的重要理念,即以主体为展开的安全体系构建。引用一段话:
在考虑应用安全时,你最常问的问题多是“当前用户是谁?”或“当前用户容许作 X 吗?”。当咱们写代码或设计用户界面时,问本身这些问题很日常:应用一般都是基于用户故事构建的,而且你但愿功能描述(和安全)是基于每一个用户的。因此,对于咱们而言,考虑应用安全的最天然方式就是基于当前用户。Shiro 的 API 用它的 Subject 概念从根本上体现了这种思考方式。
在应用程序中,咱们能够在任何地方获取当前操做的用户主体:
import org.apache.shiro.subject.Subject; import org.apache.shiro.SecurityUtils; ... Subject currentUser = SecurityUtils.getSubject();
得到Subject 后,经过这个对象,咱们能够对其进行绝大多数安全操做:登陆、登出、访问会话、执行受权检查等。
Shiro 的api很是直观,它反映了开发者以“每一个用户” 思考安全控制的天然思惟方式。
Subject 的幕后推手是 SecurityManager,Subject 表明了当前用户的安全操做,SecurityManager则管理全部用户的安全操做。
SecurityManager 是 Shiro 框架的核心,充当“保护伞”,引用了多个内部嵌套安全组件,它们造成了对象图。可是,一旦 SecurityManager 及其内部对象图配置好,它就会退居幕后,应用开发人员几乎把他们的全部时间都花在 Subject API 调用上。
一个应用只须要一个 SecurityManager,是一个单例对象。它的缺省实现是POJO,Shiro 里的其余组件也是同样。所以,能够用POJO兼容的任何配置机制进行配置:普通的Java代码、Spring xml、YAML、和 ini 文件等。基本上,可以实例化类和调用JavaBean兼容方法的任何配置形式均可以。
好比,经过ini文件进行配置:
[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
[main] 段落是配置SecurityManager 对象及其使用的其余任何对象(如 Realm) 的地方,在上面的示例中,咱们看到了两个对象:
一、cm对象,是Shiro 的HashedCredentialsMatcher 类实例,cm 的各属性经过“嵌套点”语法进行配置。
二、iniRealm对象,被 SecurityManager 用来表示以INI 格式定义的用户帐户。
而后,咱们在Java代码中,能够垂手可得的得到 SecurityManager对象了:
//1. 加载 INI 配置 Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini"); //2. 建立 SecurityManager SecurityManager securityManager = factory.getInstance(); //3. 使其可访问 SecurityUtils.setSecurityManager(securityManager);
3.三、Realm
Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“链接器”。当切实与像用户账户这类安全相关数据进行交互,执行认证(登陆)和受权(访问控制)时,Shiro 会从应用配置的 Realm 中查找不少内容。
从某种意义上讲,Realm 实际上就是一个安全相关的 DAO:它封装了数据源的链接细节,并在须要时将相关数据提供给 Shiro。
注意:在配置Shiro 时,必须指定至少一个 Realm ,能够配置多个。
Shiro 内置了一些Realm ,支持多种数据源的链接,如JDBC、LDAP、INI文件的链接等。另外,能够自定义Realm 实现,方便个性化的应用场景。
应用安全的四要素:认证、受权、会话管理、加密。
虽然有些武断,可是通常web 应用认证就是登陆功能。也就是说,当用户使用应用进行认证时,他们就在证实他们就是本身所说的那我的。
这是一个典型的三步过程:
一、收集用户身份信息,成为当事人(principal),以及身份的支持证实,称为证书(Credential)。
二、将当事人和证书提交给系统。
三、若是提交的证书与系统指望的该用户身份(当事人)匹配,该用户就被认为是通过认证的,反之则被认为未经认证的。
Shiro 以简单直观的方式支持一样的流程。Shiro 有一套以Subject 为中心的API,几乎你想要用 Shiro 在运行时完成的全部事情都能经过与当前执行的 Subject 进行交互而达成。所以,要登陆 Subject,只须要简单地调用它的 login 方法。传入表示被提交当事人和证书(在这种状况下,就是用户名和密码)的 AuthenticationToken 实例。
//1. 接受提交的当事人和证书: AuthenticationToken token = new UsernamePasswordToken(username, password); //2. 获取当前 Subject: Subject currentUser = SecurityUtils.getSubject(); //3. 登陆: currentUser.login(token);
能够看到,Shiro的操做极其简洁和天然,这也是Shiro 惯有的风格。在调用 login()方法后,SecurityManager 会收到AuthenticationToken,并将其发送给已配置的 Realm,执行必须的认证检查,以往咱们手动去数据库中进行校验和匹配的时代已通过去了,这些全部的操做,所有由Shiro 帮咱们自动完成。当数据通过Realm 的检查后发现没法匹配,那么Shiro 就会返回 AuthenticationException 异常的子类,经过这些子类,咱们能够精确的控制想要返回给用户的错误信息:
try { currentUser.login(token); } catch (IncorrectCredentialsException ice) { … } catch (LockedAccountException lae) { … } … catch (AuthenticationException ae) {… }
若是没有抛出任何异常,则证实 Subject 登陆成功,就被认为是已认证的。
受权实质上就是访问控制,控制已认证的用户可以访问应用的哪些内容,如资源、页面等。
多数用户执行访问控制是经过 角色 + 权限 的概念来完成的。角色是全部用户个体的一个分组,如管理员、普通用户、商家等;而权限 则表示具体可以操做的行为,好比查询全部用户、删除某些用户、修改信息等等,是与具体应用资源直接挂钩的。
用户、角色和 权限三者每每经过 角色 来进行转换,用户和权限之间一般不进行直接绑定:
咱们能够经过shiro的校验方法,来便捷地实现分支语句:
if ( subject.hasRole("administrator") ) { // 显示‘Create User’按钮 } else { // 按钮置灰 }
虽然,在概念上,权限与角色直接挂钩,但其最终效果仍是要落实到具体的某个用户是否具备某个权限,为此,Shiro也为咱们提供了相应的校验方法:
if ( subject.isPermitted("user:create") ) { // 显示‘Create User’按钮 } else { // 按钮置灰 }
这样,任何具备“user:create”权限的角色或用户均可以点击‘Create User’按钮,而且这些角色和指派甚至能够在运行时改变,这给你提供了一个很是灵活的安全模型。
上例中,"user:create" 字符串是一种遵循特定规则的权限描述符,具体详情可了解:http://shiro.apache.org/permissions.html
上面这些权限的调用,最终都会发送到SecurityManager中,它会咨询 Realm 作出本身的访问控制决定。必要时,还容许单个 Realm 同时响应认证和受权操做。
在以往的Servlet应用中,咱们最常使用的会话对象就是 HttpSession 对象。
在Shiro 中,也有属于本身的会话管理机制和用户的会话对象。Shiro 容许开发者在任何应用或架构层一致地使用 Session API。
它为任何应用(从小型后台独立应用到大型集群 Web 应用)提供了一个会话编程范式。这意味着,那些但愿使用会话的应用开发者,没必要被迫使用 Servlet 或 EJB 容器了。或者,若是正在使用这些容器,开发者如今也能够选择使用在任何层统一一致的会话 API,取代 Servlet 或 EJB 机制。
Shiro 会话最重要的一个好处或许就是它们是独立于容器的。这个特性的做用很是巨大,设想一下会话集群。对集群会话来说,支持容错和故障转移有多少种容器特定的方式?Tomcat 的方式与 Jetty 的不一样,而 Jetty 又和 Websphere 不同,等等。但经过 Shiro 会话,你能够得到一个容器无关的集群解决方案。
Shiro 的架构容许可插拔的会话数据存储,如企业缓存、关系数据库、NoSQL 系统等。这意味着,只要配置会话集群一次,它就会以相同的方式工做,跟部署环境无关 - Tomcat、Jetty、JEE 服务器或者独立应用。无论如何部署应用,毋须从新配置应用。
获取当前用户的Session 对象,咱们可使用下面这样的方法:
Session session = subject.getSession();
Session session = subject.getSession(boolean create);
上面这些方法在概念上等同于HttpServletRequest API。第一个方法会返回 Subject 的现有会话,或者若是尚未会话,它会建立一个新的并将之返回。第二个方法接受一个布尔参数,这个参数用于断定会话不存在时是否建立新会话。一旦得到 Shiro 的会话,你几乎能够像使用 HttpSession 同样使用它。Shiro 保留的 HttpSession 的使用体验,但不一样的是 Shiro 能够在任何应用中使用会话机制,不只限于Web应用。 Shiro Session 的一些方法:
Session session = subject.getSession();
session.getAttribute("key", someValue); Date start = session.getStartTimestamp(); Date timestamp = session.getLastAccessTime(); session.setTimeout(millis); ...
在加密方面,Shiro 尽量的简化加密处理的步骤,并让JDK的加密支持可用。
注意一点,加密并非特定于Subject 的,加密的特性是 Shiro 的一部分,但不特定于仅仅对 Subject 的处理。咱们能够在任何地方使用 Shiro 的加密支持,甚至在不使用 Subject 的状况下。
对于加密支持,Shiro 真正关心的两个领域是加密哈希(又名消息摘要)和加密密码。
传统的JDK加密操做过于复杂,并且是基于笨拙的工厂静态方法api,它并非面向对象的设计:
ry {
MessageDigest md = MessageDigest.getInstance("MD5"); md.digest(bytes); byte[] hashed = md.digest(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
若是在某些场景下频繁会用到哈希加密的状况,那么开发者就必需要封装以后才能比较轻松的使用JDK 的加密支持,不然将会是一个不小的工做量。Shiro 为咱们提供了这样的便捷,对于相似的功能,只须要简单的几行代码就能够完成,同时也省去了一些老旧的和没必要要的异常捕获:
// 面向对象的MD5加密方式 String hex = new Md5Hash(myFile).toHex(); // SHA-512 String encodedPassword = new Sha512Hash(password, salt, count).toBase64();
刚刚提到的这些是一些必要的哈希算法API,另外一个Shiro 关心的事情就是 加密密码。
咱们知道,加密是使用密钥对数据进行可逆转换的加密算法。咱们使用其保证数据的安全,尤为是传输或存储数据时,以及在数据容易被窥探的时候。
一样 JDK 的加密API也很是复杂和难用,而Shiro 经过引入它的 CipherService API 试图简化加密密码的整个概念。
CipherService 是多数开发者在保护数据时求之不得的东西:简单、无状态、线程安全的 API,可以在一次方法调用中对整个数据进行加密或解密。你所须要作的只是提供你的密钥,就可根据须要加密或解密。
AesCipherService cipherService = new AesCipherService(); cipherService.setKeySize(256); // 建立一个测试密钥: byte[] testKey = cipherService.generateNewKey(); // 加密文件的字节: byte[] encrypted = cipherService.encrypt(fileBytes, testKey);
较之 JDK 的 Cipher API,Shiro 的示例要简单的多:
Shiro 的 CipherService API 还有其余好处,如同时支持基于字节数组的加密 / 解密(称为“块”操做)和基于流的加密 / 解密(如加密音频或视频)。
Shiro 附带了一个帮助保护web应用的强健的web 支持模块,除了配置基本的依赖和Shiro 的核心单例组件 - SecurityManager 以外,可能剩下的就只是要配置一个Shiro Servlet 过滤器了。
当咱们完成了 这些前期的必要的配置工做后,Shiro Filter 就会过滤每一个请求,并确保在请求期间特定的 Subject 是可访问的。同时因为它过滤了每一个请求,你能够执行安全特定的逻辑以保证只有知足必定标准的请求才被容许经过。
Shiro 经过其创新的 URL 过滤器链功能支持安全特定的过滤规则。
[urls]
/assets/** = anon /user/signup = anon /user/** = user /rpc/rest/** = perms[rpc:invoke], authc /** = authc
对于每一行,等号左边的值表示相对上下文的 Web 应用路径。等号右边的值定义了过滤器链 - 一个逗号分隔的有序 Servlet 过滤器列表,它会针对给出的路径进行执行。每一个过滤器都是普通的 Servlet 过滤器,你看到的上面的过滤器名字(anon,user,perms,authc)是 Shiro 内置的安全相关的特殊过滤器。你能够搭配这些安全过滤器来建立高度定制的安全体验。你还能够指定任何其余现有的 Servlet 过滤器。
对于 Web 应用,Shiro 缺省将使用咱们习觉得常的 Servlet 容器会话做为其会话基础设施。即,当调用 subject.getSession() 和 subject.getSession(boolean) 方法时,Shiro 会返回 Servlet 容器的 HttpSession 实例支持的 Session 实例。
这种方式的曼妙之处在于调用 subject.getSession() 的业务层代码会跟一个 Shiro Session 实例交互 - 尚未“认识”到它正跟一个基于 Web 的 HttpSession 打交道。这在维护架构层之间的清晰隔离时,是一件很是好的事情。
另外,当须要使用与容器无关的会话特性时,咱们也能够开启Shiro 的原生会话管理。不一样于传统的HttpServletRequest.getSession()和 HttpSession API只能和Servlet 容器打交道,Shiro的原生会话管理依然可让用户使用相同的 HTTPServletRequest 和 HttpSession 调用完成与原生会话的协做,而不须要重构这些代码。这是由于 Shiro 完整实现了 Servlet规范中的 Session 部分以在 Web 应用中支持原生会话,所以开发者的全部 HTTPSession 的调用都会被委托给 Shiro 内部的原生会话 API。
Apache Shiro 框架还包含有对保护 Java 应用很是有用的其余特性,如:
Apache Shiro 是一个功能齐全、健壮、通用的 Java 安全框架,你能够用其为你的应用护航。经过简化应用安全的四个领域,即认证、受权、会话管理和加密,在真实应用中,应用安全能更容易被理解和实现。Shiro 的简单架构和兼容 JavaBean 使其几乎可以在任何环境下配置和使用。附加的 Web 支持和辅助功能,好比多线程和测试支持,让这个框架为应用安全提供了“一站式”服务。