Apache Shiro深入会话管理篇(一):使用会话与会话管理器

摘要

在前面的的入门学习中,已经基本了解Shiro会话管理基本内容,包括容器会话和本地会话,此篇,我们将深入详细的学习Shiro安全框架的会话管理,内容比较多,我们将分上下两个部分俩讲解。上篇中包括使用会话和会话管理两节,下篇包括会话集群和会话与主体状态两节。整体目录如下:

篇章目录

1、使用会话

2、会话管理

3、会话集群

4、会话与主体状态

1.概述

Apache Shiro在安全框架领域提供了独特的东西:从最简单的命令行和智能手机应用程序到最大的企业Web应用集群的完整企业级会话解决方案。

这对许多应用程序有很大的影响——直到Shiro,如果您需要会话支持,则需要将应用程序部署到Web容器中或使用EJB Stateful Session Beans。 Shiro的会话支持比这两种机制中的任何一种都更容易使用和管理的机制,并且它可以在任何应用程序中使用,无论是否有容器。

即使您将应用程序部署在Servlet或EJB容器中,仍然有令人信服的理由采用Shiro的会话支持而不是容器的会话管理。 以下是Shiro会话支持提供的最令人满意的功能特性列表:

1. 基于POJO/J2SE(IoC友好)——Shiro中的所有内容(包括会话和会话管理的所有方面)均基于接口并通过POJO实施。 这使您可以使用任何与JavaBeans兼容的配置格式(如JSON,YAML,Spring XML或类似机制)轻松配置所有会话组件。 您还可以轻松扩展Shiro的组件或根据需要编写自己的组件,以完全自定义会话管理的功能。

2. 轻松定制会话存储——由于Shiro的会话对象基于POJO,因此会话数据可以轻松存储到任意数量的数据源中。 这使您可以准确定制应用程序的会话数据驻留的位置,例如,文件系统、内存、网络分布式缓存,关系数据库或专有数据存储。

3. 独立于容器的集群!——使用Ehcache Terracotta,Coherence,GigaSpaces等易于使用的网络缓存产品,可以轻松将Shiro的会话集群。 这意味着您只需一次性配置Shiro的会话群集,无论您部署哪个容器,会话都将以相同的方式被群集。不需要特定性容器配置!

4. 异构客户端访问 ——与EJB或Web会话不同,Shiro会话可以通过各种客户端技术"共享"。 例如,桌面应用程序可以在Web应用程序中"查看"和"共享"同一用户使用的同一物理会话。 除了Shiro以外,我们还没有注意到可以支持这个功能的其它框架。

5. 事件监听器 ——事件监听器允许您在会话生命周期中监听生命周期事件。 您可以监听这些事件并对其进行响应以获取自定义应用程序行为。例如,在会话过期时更新用户记录。

6. 主机地址保留 ——Shiro会话保留发起会话的主机的IP地址或主机名。 这使您可以确定用户所在的位置并做出相应的反应(通常在IP关联是确定的Intranet环境中很有用)。

7. 闲置/过期支持 - 会话由于处于非活动状态而如期终止,但可以通过touch()方法延长会话,以便在需要时保持活动状态。 在富Internet应用程序(RIA)环境中,此用户可能正在使用桌面应用程序,但可能不定期与服务器通信,但服务器会话不应过期。

8. 透明的Web使用 ——Shiro的Web支持完全实现并支持Sessions的Servlet 2.5规范(HttpSession接口及其所有相关的API)。 这意味着您可以在现有的Web应用程序中使用Shiro会话,并且不需要更改任何现有的Web代码。

9. 可用于SSO ——由于Shiro会话是基于POJO的,因此它们可以轻松存储到任何数据源中,并且可以在需要时跨应用程序"共享"。 我们称之为'穷人的SSO',它可以用来提供简单的登录体验,因为共享会话可以保留身份验证状态。

以上所列的Shiro会话管理相关特性,基本上可以满足任何情况下的需要。

1. 使用会话

几乎像Shiro中的其他事物一样,您通过与当前正在使用执行的主体进行交互而来获得一个会话对象(Session):

currentUser.getSession() 方法是调用currentUser.getSession(true)的快捷方式。

对于那些熟悉HttpServletRequest API的人来说,Subject.getSession(boolean create)方法的功能与HttpServletRequest.getSession(boolean create)方法的功能相同:

如果Subject已有Session,则布尔参数将被忽略,会话Session将立即被返回;

如果Subject还没有会话,并且create 参数为true,则会创建并返回新的会话Session。

如果Subject尚未具有会话并且create 参数为false,则不会创建新会话并返回null。

注意:getSession调用可以在任何应用程序中工作,即使是非web应用程序也一样。

在开发框架代码时可以使用subject.getSession(false)以获得良好效果,以确保不会创建不必要Session。

一旦你获得了一个主体的会话(Session),你可以用它做很多事情,比如设置或检索属性,设置其超时等等。 查看Session JavaDoc(http://shiro.apache.org/static/current/apidocs/org/apache/shiro/session/Session.html)以查看单个会话的可能性(Session的API参考)。

2. 会话管理器(SessionManager)

SessionManager,就像它的名字可能暗示的那样,为应用程序中的所有主体(创建,删除,闲置和验证等)管理会话。与Shiro中的其他核心体系结构组件一样,SessionManager是由SecurityManager维护的顶级组件。

默认的SecurityManager实现默认使用开箱即用的DefaultSessionManager。 DefaultSessionManager实现提供了应用程序所需的所有企业级会话管理功能,如会话验证,孤立清理等。这可以在任何应用程序中使用。

提示:Web应用程序根据需要可以使用不同的SessionManager实现。具体请参阅Web文档以了解特定Web的会话管理信息(可以参考前面的文章“Web应用支持”部分描述)。

与SecurityManager管理的所有其他组件一样,SessionManager可以通过Shiro的所有SecurityManager默认实现(基于getSessionManager()/setSessionManager()方法)上的JavaBeans风格的getter/setter方法获取或设置。 或者,如果使用shiro.ini配置实现:

(在shiro.ini中配置新的会话管理)

但是从头去创建一个SessionManager是一项复杂的任务,而不是大多数人想要做的事情。 Shiro的开箱即用的SessionManager实现高度可定制且可配置,并且将满足大多数需求。 本文档的其余大部分内容都假设您在使用Shiro的默认SessionManager实现所覆盖的配置选项,但请注意,您几乎可以根据自己的意愿创建或插入任何内容。

3.1 会话超时

默认情况下,Shiro的SessionManager实现的超时时间默认为30分钟。 也就是说,如果创建的会话保持空闲状态(未使用,其lastAccessedTime未更新)持续30分钟或更长时间,则会话被视为已过期,并且不会再被使用。

您可以设置默认SessionManager实现的globalSessionTimeout属性来定义所有会话的默认超时值。 例如,如果您希望超时时间为一小时而不是30分钟,可如下操作:

(在配置文件Shiro.ini中配置默认超时时间)

每会话超时

上述globalSessionTimeout值是所有新创建的会话的默认值。 您可以通过设置单个会话的超时值来控制每个会话的会话超时。 像上面的globalSessionTimeout一样,该值是以毫秒(而不是秒)为单位的时间。

比如针对一个敏感用户的会话时间,你可以直接调用Session的API设置其时间值。

3.2 会话监听器

Shiro支持SessionListener的概念,允许您在发生重要会话事件时作出反应。 您可以实现SessionListener接口(或扩展便利的SessionListenerAdapter)并相应地对会话操作进行响应。

由于默认的SessionManager的sessionListeners属性是一个集合,因此可以为SessionManager配置一个或多个监听器实现,像在shiro.ini中的任何其他集合来配置一样。

(配置文件中的监听器配置)

注意:SessionListeners会在任何会话(Session)中发生事件时被通知,而不仅仅是特定会话。

3.3 会话存储

每当创建或更新会话时,其数据都需要保存到存储设备中,以便以后可以由应用程序访问。 同样,如果会话无效且用时过长,则需要将其从存储中删除,以便会话数据存储空间不会耗尽。 SessionManager实现将这些创建/读取/更新/删除(CRUD)操作委托给内部组件SessionDAO,该组件反映了数据访问对象(DAO)设计模式。

SessionDAO的强大之处在于您可以实现此接口与任何您希望的数据存储进行通信。 这意味着您的会话数据可以驻留在内存中、文件系统中、关系数据库或NoSQL数据存储中或您需要的任何其他位置。 您可以控制持久化行为。

您可以将任何SessionDAO实现配置为缺省SessionManager实例上的属性值。 例如,在shiro.ini中配置:

但是,正如你所期望的那样,Shiro已经有了一些很好的SessionDAO实现,你可以开箱即用或根据自己的需要子类化后再用。

Web应用注意:

上述securityManager.sessionManager.sessionDAO = $ sessionDAO配置,仅适用于使用Shiro本机会话管理器的情况。Web应用程序默认不使用本机会话管理器,而是保留Servlet容器的默认会话管理器,该会话管理器不支持SessionDAO。如果您希望在基于Web的应用程序中启用SessionDAO以用于自定义会话存储或会话群集,则必须先配置本机Web会话管理器。示例如下:

配置SessionDAO须知:

Shiro的默认配置的本地化SessionManagers只使用内存会话存储。这不适用于大多数生产应用。大多数生产应用程序都希望配置提供EHCache支持(见下文)或提供自己的SessionDAO实现。请注意,Web应用程序默认使用基于servlet容器的SessionManager,并不存在此问题。这只是使用Shiro本地SessionManager时的一个问题。

3.3.1 EHCache SessionDAO

EHCache默认情况下未启用,但如果您不打算实施自己的SessionDAO,强烈建议您为Shiro的SessionManagement启用EHCache支持。 EHCache SessionDAO会将会话存储在内存中,并在内存受限时支持溢出到磁盘。 这对生产应用程序非常有用,可确保您在运行时不会随机"丢失"会话。

推荐-1:使用EHCache作为默认值

如果您没有编写自定义的SessionDAO,那么一定要在您的Shiro配置中启用EHCache。 EHCache也可以超越会话,缓存身份验证和授权数据。 有关更多信息,请参阅缓存文档。

推荐-2:容器独立的会话集群

一旦进入类路径,这第一个shiro.ini示例将向您展示如何将EHCache用于Shiro的所有缓存需求(而不仅仅是会话支持):

(针对所有Shiro缓存需求的EhCache配置)

最后一行securityManager.cacheManager = $ cacheManager为所有Shiro需求配置一个CacheManager。 该CacheManager实例将自动传播到SessionDAO(实现CacheManagerAware接口的EnterpriseCacheSessionDAO)。

然后,当SessionManager要求EnterpriseCacheSessionDAO持久化Session时,它将使用EHCache支持的Cache实现来持久化会话数据。

Web应用注意:

不要忘记,在使用Shiro本地化SessionManager实现时,分配SessionDAO是其一项特性。 Web应用默认使用不支持SessionDAO的、基于Servlet容器的SessionManager。 如果您想在Web应用程序中使用基于Ehcache的会话存储,请按上文所述配置本机Web SessionManager。

1)EHCache会话缓存配置

默认情况下,EhCacheManager使用Shiro特定ehcache.xml文件来设置会话缓存区域和其它必要的配置,以确保会话正确存储和检索。

如果您查看默认的ehcache.xml文件,您将看到以下shiro-activeSessionCache缓存配置:

如果您希望使用自己的ehcache.xml文件,请确保您为Shiro的需求定义了类似的缓存条目。 很可能您会更改maxElementsInMemory属性值以满足您的需求。 但是,在您自己的配置中至少存在以下两个属性(并且未更改)是非常重要的:

overflowToDisk ="true"——这确保了如果用完进程内存,会话将不会丢失,并且可以序列化到磁盘

eternal ="true" ——确保缓存条目(会话实例)永不过期或由缓存自动清除。 这是必要的,因为Shiro根据预定的流程进行自己的验证(请参阅下面的"会话验证&调度")。 如果我们将其关闭,那么缓存可能会驱逐Sessions,而Shiro不知道它会导致问题。

2)EHCache会话缓存名称

默认情况下,EnterpriseCacheSessionDAO向CacheManager请求一个名为"shiro-activeSessionCache"的缓存。 如上所述,此缓存名称/区域将在ehcache.xml中进行配置。

如果您想使用其他名称而不是此默认名称,则可以在EnterpriseCacheSessionDAO上配置该名称,如下所示:

(在shiro.ini中为Shiro的活动会话缓存配置缓存名称)

只需确保ehcache.xml中的相应条目与该名称匹配,并且已经配置了overflowToDisk ="true"和eternal ="true",如上所述。

3.3.2 定制会话ID

Shiro的SessionDAO实现使用内部SessionIdGenerator组件在每次创建新会话时生成新的会话ID。 生成ID,分配给新创建的Session实例,然后Session通过SessionDAO被保存。

默认的SessionIdGenerator是一个JavaUuidSessionIdGenerator,它根据Java UUID生成字符串 ID。 该实现适用于所有生产环境。

如果这不符合您的需求,您可以实现SessionIdGenerator接口并在Shiro的SessionDAO实例上配置实现。 在shiro.ini中配置示例如下:

(配置自定义会话ID生成器)

3.4 会话验证&调度

会话必须经过验证,以便可以从会话数据存储中删除任何无效(过期或停止)会话。 这可以确保随着时间的推移,数据存储不会被再也不使用的会话占用。

出于性能方面的原因,只有验证Sessions才能了解是否在访问它们时被停止或过期(即subject.getSession())。 这意味着如果没有额外的常规定期验证,会话“孤儿”(或说孤立会话)会渐渐填满会话数据存储域。

一个常见的描述“孤立”示例的是网络浏览器场景:假设用户登录到Web应用程序,并创建会话以保留数据(身份验证状态,购物车等)。 如果用户在应用程序不知道的情况下没有注销并关闭浏览器,他们的会话基本上只是在会话数据存储中"闲置"(孤立)。 SessionManager无法检测到用户不再使用浏览器,并且会话永远不会再被访问(它是孤立的)。

会话孤儿,如果他们不经常清除,会填满会话数据存储空间(这会很糟糕)。 因此,为了防止孤儿堆积,SessionManager实现支持SessionValidationScheduler的概念。 SessionValidationScheduler负责定期验证会话,以确保在必要时对其进行清理。

3.4.1 缺省会话验证调度器(SessionValidationScheduler)

可在所有环境中使用的默认SessionValidationScheduler,它是使用JDK的ScheduledExecutorService控制验证发生频率(多久验证出现)的ExecutorServiceSessionValidationScheduler。

默认情况下,此实现将每小时执行一次验证。 您可以通过指定ExecutorServiceSessionValidationScheduler的新实例并指定不同的时间间隔(以毫秒为单位)来更改验证发生的速率,示例如下:

(shiro.ini中的ExecutorServiceSessionValidationScheduler时间间隔配置)

3.4.2 自定义SessionValidationScheduler

如果您希望提供自定义SessionValidationScheduler实现,则可以将其指定为默认SessionManager实例的属性。 在shiro.ini中配置参考示例如下:

(在shiro.ini中配置一个自定义的SessionValidationScheduler)

3.4.3 禁用会话验证

在某些情况下,您可能希望完全禁用会话验证,因为您已经在Shiro的控制之外设置了一个过程来为您执行验证。 例如,也许您正在使用企业缓存,并依靠缓存的Time To Live设置来自动清除旧会话。 或者,您可能已经设置了一个cron作业来自动清除自定义数据存储。 在这些情况下,您可以关闭会话验证调度:

(在shiro.ini中禁用会话验证调度)

会话在从会话数据存储中检索时仍然会被验证,但是这会禁用Shiro的定期验证。

注意:在某处启用会话验证时

如果关闭Shiro的会话验证调度器,则必须通过其他一些机制(cron作业等)执行定期会话验证。这是保证会话“孤儿”不会填满数据存储的唯一方法。

3.4.4 失效会话删除

如上所述,定期会话验证的目的主要是删除任何无效(过期或停止)会话,以确保它们不会填满会话数据存储。

默认情况下,每当Shiro检测到无效会话时,它都会尝试通过SessionDAO.delete(session)方法将其从基础会话数据存储中删除。 对于大多数应用程序来说,这是确保会话数据存储空间不被耗尽的良好做法。

但是,有些应用程序可能不希望Shiro自动删除会话。 例如,如果应用程序提供了支持可查询数据存储的SessionDAO,则应用程序团队可能希望旧的或无效的会话在一段时间内可用。 这将允许团队针对数据存储运行查询,以查看例如用户上周创建了多少会话,或用户会话的平均持续时间或类似的报告类型查询。

在这些情况下,您可以完全关闭无效会话删除配置。 在shiro.ini中关闭删除的示例如下:

(shiro.ini中关闭配置删除无效会话功能)

不过要小心! 如果您关闭此功能,则有责任确保会话数据存储不会耗尽其空间。 您必须自己从您的数据存储区中删除无效会话!

还要注意的是,即使您阻止Shiro删除无效会话,您仍应该以某种方式启用会话验证 - 无论是通过Shiro的现有验证机制还是通过您自己提供的自定义机制(请参阅上面的"禁用会话验证"一节以获取更多信息)。 验证机制会更新您的会话记录以反映无效状态(例如,当它失效,上次访问时等),即使您将在其他时间手动删除它们。

警告——

如果您将Shiro配置为不删除无效会话,则您有责任确保会话数据存储不会耗尽其空间。您必须自己从您的数据存储区中删除无效会话!

另请注意,禁用会话删除与禁用会话验证调度不同。您几乎应该总是使用会话验证调度机制-无论是直接支持的还是您自己的。

Ok,关于会话管理的上篇就到写这里,请继续关注下篇:会话集群及会话主体状态

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180503A0QEDM00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券