Shiro中的核心概念

Apache Shiro™ 是一个功能强大且易于使用的 Java 安全框架,用于执行身份验证、授权、加密和会话管理。借助 Shiro 易于理解的 API,您可以快速轻松地保护任何应用程序 - 从最小的移动应用程序到最大的 Web 和企业应用程序。

一、什么是 Apache Shiro?

Apache Shiro(发音为“shee-roh”,日语中“城堡”的意思)是一个功能强大且易于使用的 Java 安全框架,它执行身份验证、授权、加密和会话管理,可用于保护任何应用程序的安全 -从命令行应用程序、移动应用程序到最大的 Web 和企业应用程序。

Shiro 提供了应用程序安全 API 来执行以下方面(我喜欢将它们称为应用程序安全性的 4 个基石):

  • Authentication - 提供用户的身份认证,通常被称为用户登录。
  • Authorization - 访问控制
  • Cryptography - 保护或隐藏数据免遭窥探
  • Session Management - 每个用户的时间敏感状态

Shiro 还支持一些辅助功能,例如 Web 应用程序安全性、单元测试和多线程支持,但这些功能的存在是为了强化上述四个主要问题。

二、为什么创建Apache Shiro?

对于一个框架来说,要真正证明它的存在,并因此成为你使用它的理由,它应该满足其他替代方案无法满足的需求。为了理解这一点,我们需要看看 Shiro 的历史以及它创建时的替代方案。

在 2008 年加入 Apache 软件基金会之前,Shiro 已经有 5 岁了,之前称为 JSecurity 项目,该项目于 2003 年初启动。2003 年,Java 应用程序开发人员没有太多通用的安全替代方案 - 我们几乎只能使用 Java 身份验证和授权服务,也称为 JAAS。JAAS 有很多缺点——虽然它的身份验证功能还可以接受,但授权方面却很迟钝,使用起来令人沮丧。此外,JAAS 与虚拟机级别的安全问题密切相关,例如,确定是否应允许在 JVM 中加载类。作为一名应用程序开发人员,我更关心应用程序最终用户可以做什么,而不是我的代码在 JVM 中可以做什么。当时游戏中唯一的会话选择是 HttpSession(需要 Web 容器)和 EJB 2.1 Stateful Session Beans(需要 EJB 容器)。

由于我当时使用的应用程序,我还需要访问一个干净的、与容器无关的会话机制。我需要一些可以与容器分离的东西,可以在我选择的任何环境中使用。

最后是密码学问题。有时我们都需要保证数据安全,但 Java 加密架构很难理解,除非你是加密专家。该 API 充满了检查异常,使用起来很麻烦。我希望有一个更干净、开箱即用的解决方案,可以根据需要轻松加密和解密数据。

因此,看看 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 从根本上体现了其主题概念中的这种思维方式。

    “主题”一词是一个安全术语,基本上意味着“当前正在执行的用户”。它只是不被称为“用户”,因为“用户”一词通常与人类相关联。在安全领域,术语“主题”可以指人类,也可以指第 3 方进程、守护程序帐户或任何类似的东西。它只是意味着“当前正在与软件交互的事物”。不过,对于大多数意图和目的,您可以将其视为 Shiro 的“用户”概念。您可以轻松地在代码中的任何位置获取 Shiro 主题,如下面的清单 1 所示。

    一旦您获取了主题,您就可以立即访问当前用户想要使用 Shiro 执行的 90% 的操作,例如登录、注销、访问其会话、执行授权检查等等 - 稍后会详细介绍。这里的关键点是 Shiro 的 API 很大程度上是直观的,因为它反映了开发人员思考“每个用户”安全控制的自然倾向。在代码中的任何位置访问主题也很容易,从而允许安全操作在需要的任何地方进行。

    清单1:获取主题

    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.SecurityUtils;
    ...
    Subject currentUser = SecurityUtils.getSubject();
  • SecurityManager

    主题的“幕后”对应者是 SecurityManager。虽然主题代表当前用户的安全操作,但安全管理器管理所有用户的安全操作。它是 Shiro 架构的核心,充当一种“伞”对象,引用许多形成对象图的内部嵌套安全组件。然而,一旦配置了 SecurityManager 及其内部对象图,通常就不再管它了,应用程序开发人员几乎将所有时间都花在了主题 API 上。

    那么如何设置SecurityManager呢?嗯,这取决于你的应用环境。例如,Web 应用程序通常会在 web.xml 中指定 Shiro Servlet Filter,这将设置 SecurityManager 实例。如果您运行的是独立应用程序,则需要以其他方式配置它。但有很多配置选项。

    每个应用程序几乎总是有一个 SecurityManager 实例。它本质上是一个应用程序单例(尽管它不需要是静态单例)。与 Shiro 中的几乎所有内容一样,默认的 SecurityManager 实现是 POJO,并且可以使用任何 POJO 兼容的配置机制进行配置 - 普通 Java 代码、Spring XML、YAML、.properties 和 .ini 文件等。基本上,任何能够实例化类并调用 JavaBeans 兼容方法的东西都可以使用。

    为此,Shiro 通过基于文本的 INI 配置提供了默认的“共同点”解决方案。INI 易于阅读、使用简单,并且需要很少的依赖项。您还将看到,通过对对象图导航的简单理解,可以有效地使用 INI 来配置简单的对象图,例如 SecurityManager。请注意,Shiro 还支持 Spring XML 配置和其他替代方案,但我们将在这里介绍 INI。基于 INI 配置 Shiro 的最简单示例如下面的清单 2 中的示例所示。

    清单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对象和/或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;
    
    // 加载INI配置
    Factory factory =
    new IniSecurityManagerFactory("classpath:shiro.ini");
    // 创建SecurityManager
    SecurityManager securityManager = factory.getInstance();
    // 使其易于访问
    SecurityUtils.setSecurityManager(securityManager);
    
    

    在清单 3 中,我们在这个简单示例中看到了一个三步过程:

    1. 加载将配置 SecurityManager 及其组成组件的 INI 配置。
    2. 根据配置创建 SecurityManager 实例(使用代表工厂方法设计模式的 Shiro 工厂概念)。
    3. 使 SecurityManager 单例可供应用程序访问。在这个简单的示例中,我们将其设置为 VM 静态单例,但这通常是不必要的 - 您的应用程序配置机制可以确定您是否需要使用静态内存。
  • Realms

    Shiro 中的第三个也是最后一个核心概念是Realm。Realm 充当 Shiro 和应用程序安全数据之间的“桥梁”或“连接器”。也就是说,当实际与安全相关数据(例如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro 从为应用程序配置的一个或多个领域中查找其中的许多内容。

    从这个意义上说,Realm 本质上是一个特定于安全性的 DAO:它封装了数据源的连接详细信息,并使相关数据根据需要可供 Shiro 使用。配置 Shiro 时,您必须至少指定一个用于身份验证和/或授权的 Realm。可以配置多个 Realm,但至少需要一个。

    Shiro 提供开箱即用的 Realms 来连接到许多安全数据源(也称为目录),例如 LDAP、关系数据库 (JDBC)、文本配置源(例如 INI 和属性文件)等等。如果默认 Realm 不能满足您的需求,您可以插入自己的 Realm 实现来表示自定义数据源。下面的清单 4 是配置 Shiro(通过 INI)以使用 LDAP 目录作为应用程序的领域之一的示例。

    清单4:连接到 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 环境,让我们讨论一下作为开发人员如何使用该框架。

六、Authentication

身份验证是验证用户身份的过程。也就是说,当用户使用应用程序进行身份验证时,他们就证明自己确实是他们所说的人。这有时也称为“登录”。这通常是一个三步过程。

  1. 收集用户的身份信息(称为主体)和支持身份证明(称为凭据)。
  2. 将主体和凭证提交到系统。
  3. 如果提交的凭据与系统对该用户身份(主体)的期望相匹配,则该用户被视为已通过身份验证。如果不匹配,则用户不会被视为已通过身份验证。

每个人都熟悉的此过程的一个常见示例是用户名/密码组合。当大多数用户登录软件应用程序时,他们通常会提供用户名(主体)和支持密码(凭据)。如果系统中存储的密码(或其表示形式)与用户指定的内容匹配,则认为他们已通过身份验证。

Shiro 以简单直观的方式支持相同的工作流程。正如我们所说,Shiro 有一个以主题为中心的 API - 几乎您在运行时使用 Shiro 做的所有事情都是通过与当前执行的主题交互来实现的。因此,要登录主题,您只需调用其登录方法,传递代表提交的主体和凭据(在本例中为用户名和密码)的 AuthenticationToken 实例。下面的清单 5 显示了这个示例。

清单5:主题登录

//1. 获取提交的主体和凭证:
AuthenticationToken token =
new UsernamePasswordToken(username, password);

//2. 获取当前主题:
Subject currentUser = SecurityUtils.getSubject();
//3. 登录:
currentUser.login(token);

正如您所看到的,Shiro 的 API 很容易反映常见的工作流程。您将继续将这种简单性视为所有主题操作的主题。当调用登录方法时,安全管理器将接收身份验证令牌并将其分派到一个或多个配置的领域,以允许每个领域根据需要执行身份验证检查。每个领域都能够根据需要对提交的 AuthenticationToken 做出反应。但是如果登录尝试失败会发生什么?如果用户指定了错误的密码怎么办?您可以通过对 Shiro 的运行时 AuthenticationException 做出反应来处理故障,如清单 6 所示。

清单6:处理登录失败

try {
    currentUser.login(token);
} catch (IncorrectCredentialsException ice) { …
} catch (LockedAccountException lae) { …
}
…
catch (AuthenticationException ae) {…
}

您可以选择捕获 AuthenticationException 子类之一并做出具体反应,或者一般性地处理任何 AuthenticationException(例如,向用户显示通用的“用户名或密码不正确”消息)。您可以根据您的应用要求进行选择。

主题成功登录后,他们被视为已通过身份验证,通常您允许他们使用您的应用程序。但仅仅因为用户证明了他们的身份并不意味着他们可以在您的应用程序中做任何他们想做的事情。这就引出了下一个问题:“我如何控制用户可以做什么或不可以做什么?”决定允许用户做什么称为授权。接下来我们将介绍 Shiro 如何启用授权。

七、Authorization

授权本质上是访问控制——控制用户可以在应用程序中访问哪些内容,例如资源、网页等。大多数用户通过使用角色和权限等概念来执行访问控制。也就是说,通常允许用户执行或不执行某些操作,具体取决于分配给他们的角色和/或权限。然后,您的应用程序可以根据对这些角色和权限的检查来控制公开哪些功能。正如您所期望的,Subject API 允许您非常轻松地执行角色和权限检查。例如,清单 7 中的代码片段显示了如何检查主题是否已被分配特定角色。

清单7:角色检查

if ( subject.hasRole(“administrator”) ) {
    //show the ‘Create User’ button
} else {
    //grey-out the button?
} 

如您所见,您的应用程序可以根据访问控制检查启用或禁用功能。

权限检查是执行授权的另一种方式。如上例所示检查角色存在一个重大缺陷:您无法在运行时添加或删除角色。您的代码是使用角色名称进行硬编码的,因此如果您更改了角色名称和/或配置,您的代码将会被破坏!如果您需要能够在运行时更改角色的含义,或者根据需要添加或删除角色,则必须依赖其他东西。

为此,Shiro 支持其权限概念。权限是功能的原始声明,例如“打开一扇门”、“创建博客条目”、“删除“jsmith”用户”等。通过让权限反映应用程序的原始功能,您只需在更改应用程序的功能时更改权限检查。反过来,您可以在运行时根据需要向角色或用户分配权限。作为示例,如下面的清单 8 所示,我们可以重写您之前的角色检查并改为使用权限检查。

清单8:权限检查

if ( subject.isPermitted(“user:create”) ) {
    //show the ‘Create User’ button
} else {
    //grey-out the button?
} 

这样,任何分配了“user:create”权限的角色或用户都可以单击“创建用户”按钮,并且这些角色和分配甚至可以在运行时更改,从而为您提供非常灵活的安全模型。

“user:create”字符串是遵循某些解析约定的权限字符串的示例。Shiro 通过其 WildcardPermission 开箱即用地支持此约定。尽管超出了本文的介绍范围,但您将看到 WildcardPermission 在创建安全策略时非常灵活,甚至支持实例级访问控制等功能。

清单9:实例级权限检查

if ( subject.isPermitted(“user:delete:jsmith”) ) {
    //delete the ‘jsmith’ user
} else {
    //don’t delete ‘jsmith’
}

此示例表明,如果需要,您可以控制对各个资源的访问,甚至可以控制到非常细粒度的实例级别。如果您愿意,您甚至可以发明自己的权限语法。有关更多信息,请参阅 Shiro 权限文档。最后,就像身份验证一样,上述调用最终会到达 SecurityManager,SecurityManager 将咨询一个或多个领域来做出访问控制决策。这允许领域根据需要响应身份验证和授权操作。

这就是 Shiro 授权功能的简要概述。虽然大多数安全框架仅限于身份验证和授权,但 Shiro 提供了更多功能。接下来我们将讨论 Shiro 的高级会话管理功能。

八、Session Management

Apache Shiro提供了世界上独一无二的安全框架:一个可用于任何应用程序和任何架构层的一致的会话API。也就是说,Shiro 为任何应用程序启用了会话编程范例 - 从小型守护程序独立应用程序到最大的集群 Web 应用程序。这意味着希望使用会话的应用程序开发人员不再被迫使用 Servlet 或 EJB 容器(如果他们不需要)。或者,如果使用这些容器,开发人员现在可以选择在任何层中使用统一且一致的会话 API,而不是 servlet 或 EJB 特定的机制。

但也许 Shiro 会话最重要的好处之一是它们独立于容器。这具有微妙但极其强大的含义。例如,让我们考虑会话集群。有多少种特定于容器的方法来集群会话以实现容错和故障转移? Tomcat 的做法与 Jetty 不同,Jetty 的做法又与 Websphere 等不同。但通过 Shiro 会话,您可以获得独立于容器的集群解决方案。 Shiro 的架构允许可插入的会话数据存储,例如企业缓存、关系数据库、NoSQL 系统等。这意味着您只需配置一次会话集群,无论您的部署环境如何(Tomcat、Jetty、JEE Server 或独立应用程序),它都会以相同的方式工作。无需根据您部署应用程序的方式重新配置您的应用程序。Shiro 会话的另一个好处是,如果需要,可以跨客户端技术共享会话数据。例如,如果需要,Swing 桌面客户端可以参与同一个 Web 应用程序会话 - 如果最终用户同时使用两者,这很有用。那么如何在任何环境中访问主题的会话呢?有两种主题方法,如下例所示。

清单10:主题会话

Session session = subject.getSession();
Session session = subject.getSession(boolean create);

正如您所看到的,这些方法在概念上与 HttpServletRequest API 相同。第一个方法将返回主题的现有会话,或者如果还没有会话,它将创建一个新会话并返回它。第二种方法接受一个布尔参数,用于确定是否创建新会话(如果尚不存在)。一旦获得了主题的会话,就可以像使用 HttpSession 一样使用它。 Shiro 团队认为 HttpSession API 对于 Java 开发人员来说是最舒服的,因此我们保留了它的大部分感觉。当然,最大的区别是您可以在任何应用程序中使用 Shiro Sessions,而不仅仅是 Web 应用程序。清单 11 显示了这种熟悉性。

清单11:会话方法

Session session = subject.getSession();
session.getAttribute(“key”, someValue);
Date start = session.getStartTimestamp();
Date timestamp = session.getLastAccessTime();
session.setTimeout(millis);

九、Cryptography

密码学是隐藏或混淆数据的过程,使窥探者无法理解它。 Shiro 在密码学方面的目标是简化并提供 JDK 的密码学支持。

值得注意的是,密码学通常并不特定于主题,因此它是 Shiro API 中不特定于主题的一个领域。您可以在任何地方使用 Shiro 的加密支持,即使没有使用主题。Shiro 真正关注加密支持的两个领域是加密哈希(也称为消息摘要)和加密密码。让我们更详细地看看这两个。

  • 哈希

    如果您使用过 JDK 的 MessageDigest 类,您很快就会意识到它使用起来有点麻烦。它有一个笨拙的基于静态方法工厂的 API,而不是面向对象的 API,并且您被迫捕获可能永远不需要捕获的已检查异常。如果您需要对消息摘要输出进行十六进制编码或 Base64 编码,则只能靠您自己了——两者都没有标准的 JDK 支持。 Shiro 通过干净直观的哈希 API 解决了这些问题。

    例如,让我们考虑一下相对常见的情况,即对文件进行 MD5 哈希处理并确定该哈希值的十六进制值。这称为“校验和”,在提供文件下载时经常使用 - 用户可以对下载的文件执行自己的 MD5 哈希,并断言其校验和与下载网站上的校验和匹配。如果它们匹配,用户就可以充分假设文件在传输过程中没有被篡改。

    以下是您在没有 Shiro 的情况下尝试执行此操作的方法:

    1. 将文件转换为字节数组。 JDK 中没有任何东西可以帮助解决这个问题,因此您需要创建一个辅助方法来打开 FileInputStream、使用字节缓冲区并抛出适当的 IOException 等。
    2. 使用 MessageDigest 类对字节数组进行哈希处理,处理适当的异常,如下面的清单 12 所示。
    3. 将哈希字节数组编码为十六进制字符。 JDK 中也没有任何东西可以帮助解决这个问题,因此您需要创建另一个辅助方法,并且可能在实现中使用按位运算和位移。

    清单12:JDK 的 MessageDigest

    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 类,您就会知道它可能是一头难以驯服的极其复杂的野兽。对于初学者来说,每个可能的 Cipher 配置始终由 javax.crypto.Cipher 的实例表示。需要进行公钥/私钥加密吗?你使用密码。需要使用分组密码进行流操作?你使用密码。需要创建 AES 256 位密码来保护数据?你使用密码。你明白了。

    以及如何创建您需要的 Cipher 实例?您创建一个复杂的、不直观的、以标记分隔的密码选项字符串,称为“转换字符串”,并将该字符串传递给 Cipher.getInstance 静态工厂方法。使用这种密码选项字符串方法,没有类型安全性来确保您使用有效的选项。这也隐含地意味着没有 JavaDoc 来帮助您理解相关选项。即使您知道配置是正确的,您还需要处理已检查的异常,以防您的字符串格式不正确。正如您所看到的,使用 JDK Ciphers 是一项相当繁琐的任务。这些技术很久以前就曾经是 Java API 的标准,但时代已经改变,我们需要一种更简单的方法。

    Shiro 试图通过引入其 CipherService API 来简化加密密码的整个概念。 CipherService 是大多数开发人员在保护数据时想要的:一种简单、无状态、线程安全的 API,可以在一个方法调用中完整地加密或解密数据。您所需要做的就是提供您的密钥,然后您可以根据需要进行加密或解密。例如,可以使用 256 位 AES 加密,如下面的清单 13 所示。

    清单13:Apache Shiro 加密 API

    AesCipherService cipherService = new AesCipherService();
    cipherService.setKeySize(256);
    //create a test key:
    byte[] testKey = cipherService.generateNewKey();
    //encrypt a file’s bytes:
    byte[] encrypted =
        cipherService.encrypt(fileBytes, testKey);

    与 JDK 的 Cipher API 相比,Shiro 示例更简单:

    • 您可以直接实例化 CipherService - 没有奇怪或令人困惑的工厂方法。
    • 密码配置选项表示为与 JavaBeans 兼容的 getter 和 setter - 不存在奇怪且难以理解的“转换字符串”。
    • 加密和解密在单个方法调用中执行。
    • 没有强制检查异常。如果需要,可以捕获 Shiro 的 CryptoException。

    Shiro 的 CipherService API 还有其他好处,例如能够支持基于字节数组的加密/解密(称为“块”操作)以及基于流的加密/解密(例如,加密音频或视频)。

    Java 密码学并不需要很痛苦。 Shiro 的加密支持旨在简化您保护数据安全的工作。

十、Web Support

最后但并非最不重要的一点是,我们将简要介绍 Shiro 的 Web 支持。 Shiro 附带强大的 Web 支持模块,以帮助保护 Web 应用程序的安全。为 Web 应用程序设置 Shiro 非常简单。唯一需要做的就是在 web.xml 中定义 Shiro Servlet Filter。清单 14 显示了此代码。

清单14:web.xml 中的 ShiroFilter


    ShiroFilter
    
        org.apache.shiro.web.servlet.IniShiroFilter
    
     



     ShiroFilter
     /*

此过滤器可以读取上述 shiro.ini 配置,因此无论部署环境如何,您都可以获得一致的配置体验。配置完成后,Shiro Filter 将过滤每个请求,并确保请求特定的主题在请求期间可访问。由于它会过滤每个请求,因此您可以执行特定于安全性的逻辑,以确保只允许满足特定条件的请求通过。

  • URL特定的过滤器链

    Shiro 通过其创新的 URL 过滤器链接功能支持特定于安全的过滤规则。它允许您为任何匹配的 URL 模式指定临时过滤器链。这意味着您在使用 Shiro 的过滤机制执行安全规则(或规则组合)时具有很大的灵活性 - 比单独在 web.xml 中定义过滤器要灵活得多。清单 15 显示了 Shiro INI 中的配置片段。

    清单15:特定于路径的过滤器链

    [urls]
    /assets/** = anon
    /user/signup = anon
    /user/** = user
    /rpc/rest/** = perms[rpc:invoke], authc
    /** = authc

    正如您所看到的,有一个 [urls] INI 部分可供 Web 应用程序使用。对于每一行,等号左侧的值表示上下文相关的 Web 应用程序路径。右侧的值定义了一个过滤器链 - 一个有序的、以逗号分隔的 Servlet 过滤器列表,要针对给定路径执行。每个过滤器都是普通的 Servlet 过滤器,但您在上面看到的过滤器名称(anon、user、perms、authc)是 Shiro 开箱即用提供的特殊安全相关过滤器。您可以混合搭配这些安全过滤器来创建非常自定义的安全体验。您还可以指定任何其他现有的 Servlet 过滤器。

    与使用 web.xml 相比,这要好得多,在 web.xml 中定义一个过滤器块,然后定义一个单独的断开连接的过滤器模式块?使用 Shiro 的方法,可以更轻松地准确查看针对给定匹配路径执行的过滤器链。如果您愿意,您可以在 web.xml 中仅定义 Shiro 过滤器,并在 shiro.ini 中定义所有其他过滤器和过滤器链,以获得比 web.xml 更简洁且易于理解的过滤器链定义机制。即使您没有使用 Shiro 的任何安全功能,仅这一点小小的便利就足以让 Shiro 值得使用。

  • JSP标签库

    Shiro 还提供了一个 JSP 标签库,允许您根据当前主题的状态控制 JSP 页面的输出。一个常见的例子是在用户登录后显示“Hello ”文本。但如果他们是匿名的,您可能想显示其他内容,例如“您好!今天就注册!”反而。清单 16 显示了如何使用 Shiro 的 JSP 标记来支持这一点。

    清单16:JSP 标签库示例

    <%@ taglib prefix="shiro"
        uri="http://shiro.apache.org/tags" %>
    ...
    

    Hello ! ! Register today!

    还有其他标签,允许您根据他们具有(或不具有)的角色、分配(或未分配)哪些权限以及他们是否经过身份验证、是否通过“记住我”服务记住或通过“记住我”服务来包含输出。匿名客人。

    Shiro 支持许多其他特定于 Web 的功能,例如简单的“记住我”服务、REST 和 BASIC 身份验证,当然还有透明的 HttpSession 支持(如果您想使用 Shiro 的本机企业会话)。请参阅 Apache Shiro Web 文档了解更多信息。

  • 网络会话管理

    最后,有趣的是 Shiro 对 Web 环境中会话的支持。

    • 默认 Http 会话

      对于 Web 应用程序,Shiro 默认其会话基础结构使用我们都习惯的现有 Servlet 容器会话。也就是说,当您调用方法 subject.getSession() 和 subject.getSession(boolean) 时,Shiro 将返回由 Servlet 容器的 HttpSession 实例支持的 Session 实例。这种方法的优点在于,调用 subject.getSession() 的业务层代码与 Shiro Session 实例进行交互 - 它不“知道”它正在使用基于 Web 的 HttpSession 对象。当保持跨架构层的清晰分离时,这是一件非常好的事情。

    • Shiro 在 Web 层的本机会话

      如果您在 Web 应用程序中启用了 Shiro 的本机会话管理,因为您需要 Shiro 的企业会话功能(例如独立于容器的集群),那么您当然希望 HttpServletRequest.getSession() 和 HttpSession API 能够与“本机”会话一起工作,并且不是 servlet 容器会话。如果您必须重构任何使用 HttpServletRequest 和 HttpSession API 的代码来代替使用 Shiro 的 Session API,那将是非常令人沮丧的。白当然不会想到你会这么做。相反,Shiro 完全实现了 Servlet 规范的 Session 部分,以支持 Web 应用程序中的本机会话。这意味着每当您调用相应的 HttpServletRequest 或 HttpSession 方法调用时,Shiro 都会将这些调用委托给其内部本机 Session API。最终结果是,即使您使用 Shiro 的“本机”企业会话管理,您也不必更改 Web 代码 - 这确实是一个非常方便(且必要)的功能。

十一、附加功能

Apache Shiro 框架中还有其他功能对于保护 Java 应用程序很有用,例如:

  • 用于跨线程维护主题的线程和并发支持(Executor 和 ExecutorService 支持)
  • Callable 和 Runnable 支持作为特定主题执行逻辑
  • “运行方式”支持假设另一个主体的身份(例如在管理应用程序中有用)
  • 测试工具支持,使得在单元测试和集成测试中对 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(可能还有混合)客户端支持
  • 验证码支持
  • 更轻松地配置 100% 无状态应用程序(例如许多 REST 环境)。
  • 通过请求/响应协议进行多阶段身份验证。
  • 通过 AuthorizationRequest 进行粗粒度授权。
  • 用于安全断言查询的 ANTLR 语法(例如 (‘role(admin) && (guest || !group(developer))’)

十四、总结

Apache Shiro 是一个功能齐全、强大且通用的 Java 安全框架,可用于保护您的应用程序。通过简化应用程序安全的四个领域,即身份验证、授权、会话管理和密码学,应用程序安全性在实际应用程序中更容易理解和实现。 Shiro 的简单架构和 JavaBeans 兼容性使其几乎可以在任何环境中配置和使用。额外的 Web 支持和辅助功能(如多线程和测试支持)完善了框架,为您的应用程序安全提供了“一站式服务”。 Apache Shiro 的开发团队不断前进,完善代码库并支持社区。随着开源和商业应用的持续发展,Shiro 有望变得更加强大。

十五、资源

你可能感兴趣的:(后端shirojava)