您好,登录后才能下订单哦!
本篇文章给大家分享的是有关怎样让Apache Shiro保护你的应用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
* authentication : 认证 * authorization : 授权 * cryptography : 密码学
Apache Shiro 是一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密。
在考虑应用安全时,你最常问的问题可能是“当前用户是谁?”或“当前用户允许做 X 吗?”。
对于我们而言,考虑应用安全的最自然方式就是基于当前用户。Shiro 的 API 用它的 Subject 概念从根本上体现了这种思考方式。
Subject一词是一个安全术语,其基本意思是“当前的操作用户”。称之为“用户”并不准确,因为“用户”一词通常跟人相关。在安全领域,术语“Subject”可以是人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着**“当前跟软件交互的东西”**。但考虑到大多数目的和用途,你可以把它认为是 Shiro 的“用户”概念。在代码的任何地方,你都能轻易的获得 Shiro Subject。
import org.apache.shiro.subject.Subject; import org.apache.shiro.SecurityUtils; ... Subject currentUser = SecurityUtils.getSubject();
一旦获得 Subject,你就可以立即获得你希望用 Shiro 为当前用户做的 90% 的事情,如登录、登出、访问会话、执行授权检查等。
Subject
代表了当前用户的安全操作,SecurityManager
则管理所有用户的安全操作。它是Shiro
框架的核心,充当“保护伞”,引用了多个内部嵌套安全组件,它们形成了对象图。但是,一旦 SecurityManager 及其内部对象图配置好,它就会退居幕后,我们只负责调用Subject API 就好。
这要看应用的环境。例如,Web
应用通常会在Web.xml
中指定一个Shiro Servlet Filter
,这会创建 SecurityManager
实例。
Realm
充当了Shiro
与应用安全数据间的**“桥梁”或者“连接器”。也就是说,当切实与像用户帐户这类安全相关数据进行交互,执行认证(登录)和授权(访问控制)时,Shiro
会从应用配置的Realm
中查找很多内容**。
从这个意义上讲,Realm
实质上是一个安全相关的DAO
它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro
。当配置Shiro
时,你必须至少指定一个Realm
,用于认证和(或)授权。配置多个 Realm 是可以的,但是至少需要一个。
认证是核实用户身份的过程。有时这也理解为“登录”。它是一个典型的三步骤过程。
收集用户的身份信息,称为当事人(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 的 API 很容易地就反映了这个常见流程。你将会在所有的 Subject 操作中继续看到这种简单风格。在调用了 login 方法后,SecurityManager 会收到 AuthenticationToken,并将其发送给已配置的 Realm,执行必须的认证检查。每个 Realm 都能在必要时对提交的 AuthenticationTokens 作出反应。
如果登录失败了会发生什么?如果用户提供了错误密码又会发生什么?通过对 Shiro 的运行时 AuthenticationException 做出反应,你可以控制失败
//3. 登录: try { currentUser.login(token); } catch (IncorrectCredentialsException ice) { … } catch (LockedAccountException lae) { … } … catch (AuthenticationException ae) {… }
Subject 登录成功后,他们就被认为是已认证的,通常你会允许他们使用你的应用。但是仅仅证明了一个用户的身份并不意味着他们可以对你的应用为所欲为。这就引出了另一个问题,“我如何控制用户能做或不能做哪些事情?”,决定用户允许做哪些事情的过程被称为授权。
授权实质上就是访问控制 - 控制用户能够访问应用中的哪些内容,比如资源、Web 页面等等。多数用户执行访问控制是通过使用诸如角色和权限这类概念完成的。也就是说,通常用户允许或不允许做的事情是根据分配给他们的角色或权限决定的。那么,通过检查这些角色和权限,你的应用程序就可以控制哪些功能是可以暴露的。Subject API 让你可以很容易的执行角色和权限检查。
if ( subject.hasRole(“administrator”) ) { // 显示‘Create User’按钮 } else { // 按钮置灰? }
如你所见,你的应用程序可基于访问控制检查打开或关闭某些功能。
权限检查是执行授权的另一种方法。上例中的角色检查有个很大的缺陷:你无法在运行时增删角色。角色名字在这里是硬编码,所以,如果你修改了角色名字或配置,你的代码就会乱套!如果你需要在运行时改变角色含义,或想要增删角色,你必须另辟蹊径。
为此,Shiro 支持了权限(permissions)概念。权限是功能的原始表述,如‘开门’,‘创建一个博文’,‘删除‘jsmith’用户’等。通过让权限反映应用的原始功能,在改变应用功能时,你只需要改变权限检查。进而,你可以在运行时按需将权限分配给角色或用户。
我们可以重写之前的用户检查,取而代之使用权限检查。
if ( subject.isPermitted(“user:create”) ) { // 显示‘Create User’按钮 } else { // 按钮置灰? }
这样,任何具有**“user:create”**权限的角色或用户都可以点击‘Create User’按钮,并且这些角色和指派甚至可以在运行时改变,这给你提供了一个非常灵活的安全模型。
“user:create”字符串是一个权限字符串的例子,它遵循特定的解析惯例。Shiro 借助它的 WildcardPermission
支持这种开箱即用的惯例。尽管这超出了本文的范围,你会看到在创建安全策略时,WildcardPermission 非常灵活,甚至支持像实例级别访问控制这样的功能。
if ( subject.isPermitted(“user:delete:jsmith”) ) { // 删除‘jsmith’用户 } else { // 不删除‘jsmith’ }
操作subject,最终会转向SecurityManager,它会咨询Realm,做出自己的访问控制决定
必要时,允许单个realm同时响应认证和授权
在安全框架领域,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 的两个方法。
Session session = subject.getSession(); Session session = subject.getSession(boolean create);
这些方法在概念上等同于 HttpServletRequest API。第一个方法会返回 Subject 的现有会话,或者如果还没有会话,它会创建一个新的并将之返回。第二个方法接受一个布尔参数,这个参数用于判定会话不存在时是否创建新会话。一旦获得 Shiro 的会话,你几乎可以像使用 HttpSession 一样使用它。Shiro 团队觉得对于 Java 开发者,HttpSession API 用起来太舒服了,所以我们保留了它的很多感觉。当然,最大的不同在于,你可以在任何应用中使用 Shiro 会话,不仅限于 Web 应用。
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 哈希。如果它们匹配,用户完全可以认定文件在传输过程中没有被篡改。
JDK 中没有干这事的,故而你需要创建一个辅助方法用于打开FileInputStream,使用字节缓存区,并抛出相关的 IOExceptions,等等。
try { MessageDigest md = MessageDigest.getInstance("MD5"); md.digest(bytes); byte[] hashed = md.digest(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
JDK 中还是没有干这事的,你依旧需要创建另外一个辅助方法,有可能在你的实现中会使用位操作和位移动。
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,能够在一次方法调用中对整个数据进行加密或解密。你所需要做的只是提供你的密钥,就可根据需要加密或解密。
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 支持就是为了减少你在确保数据安全上付出的努力。
Shiro 附带了一个帮助保护 Web 应用的强建的 Web 支持模块。对于 Web 应用,安装 Shiro 很简单。唯一需要做的就是在 web.xml 中定义一个 Shiro Servlet 过滤器。
<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
是可访问的。同时由于它过滤了每个请求,你可以执行安全特定的逻辑以保证只有满足一定标准的请求才被允许通过。
Shiro 通过其创新的**URL
过滤器链**功能支持安全特定的过滤规则。它允许你为任何匹配的 URL 模式指定非正式的过滤器链。这意味着, 使用 Shiro 的过滤器机制,你可以很灵活的强制安全规则(或者规则的组合) - 其程度远远超过你单独在 web.xml 中定义过滤器时所获得的。
[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。
Shiro 还提供了 JSP 标签库,允许你根据当前 Subject 的状态控制 JSP 页面的输出。一个有用的常见示例是在用户登录后显示“Hello <username>"文本。但若是匿名用户,你可能想要显示其他内容,如换而显示“Hello! Register Today!”。
<%@ 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 文档可以了解更多内容。
最后值得一提的是 Shiro 在 Web 环境中对会话的支持。
对于 Web 应用,Shiro 缺省将使用我们习以为常的 Servlet 容器会话作为其会话基础设施。即,当你调用 subject.getSession() 和 subject.getSession(boolean) 方法时,Shiro 会返回 Servlet 容器的 HttpSession 实例支持的 Session 实例。这种方式的曼妙之处在于调用 subject.getSession() 的业务层代码会跟一个 Shiro Session 实例交互 - 还没有“认识”到它正跟一个基于 Web 的 HttpSession 打交道。这在维护架构层之间的清晰隔离时,是一件非常好的事情。
如果你由于需要 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保护你的应用,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。