zoukankan      html  css  js  c++  java
  • 分布式平台下的HS(HighSecurity)会话管理

    会话管理

      在安全框架领域,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 "文本。但若是匿名用户,你可能想要显示其他内容,如换而显示“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。显示注册链接: --> 
    <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会继续发展壮大。

  • 相关阅读:
    第四节 修改表结构之alter
    第三节 数据表的创建和相关的一些指令
    第二节 数据库的创建以及相关命令
    运维不容错过的4个关键指标!
    服务器监控之 ping 监控
    Docker 监控- Prometheus VS Cloud Insight
    几种 Docker 监控工具对比
    临阵磨枪,血拼季网站优化的最后三板斧
    Java 8 vs. Scala(二):Stream vs. Collection
    Java 8 vs. Scala(一): Lambda表达式
  • 原文地址:https://www.cnblogs.com/jerryxing/p/2531225.html
Copyright © 2011-2022 走看看