Session Management

Apache Shiro 在安全框架世界中提供了一些独特的功能:适用于任何应用程序的完整企业级 Session 解决方案,从最简单的命令行和智能手机应用程序到最大的群集企业 Web 应用程序。

这对许多应用程序都有很大的影响-在 Shiro 之前,如果需要会话支持,则需要将应用程序部署在 Web 容器中或使用 EJB 状态会话 Bean。 Shiro 的 Session 支持比这两种机制中的任何一种都更易于使用和 Management,并且在任何应用程序中都可以使用,而不论其容器如何。

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

Features

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

  • 轻松自定义会话存储 -由于 Shiro 的 Session 对象基于 POJO,因此可以轻松地将会话数据存储在任意数量的数据源中。这使您可以精确地自定义应用程序的会话数据所在的位置,例如,文件系统,内存,网络分布式缓存,关系数据库或专有数据存储中的文件系统。

  • 与容器无关的群集! -使用任何易于使用的联网缓存产品(例如 Ehcache Terracotta,Coherence,GigaSpaces 等),都可以轻松地对 Shiro 的会话进行群集。等这意味着您只能为 Shiro 配置一次会话群集,并且无论将部署到哪个容器,您的会话都将以相同的方式群集。无需特定于容器的配置!

  • 异构 Client 端访问 -与 EJB 或 Web 会话不同,可以在各种 Client 端技术之间“共享” Shiro 会话。例如,桌面应用程序可以“查看”和“共享” Web 应用程序中同一用户使用的同一物理会话。除了 Shiro 之外,我们没有其他可以支持此功能的框架。

  • 事件监听器 -事件监听器允许您在会话的生存期内监听生命周期事件。您可以侦听这些事件并对它们做出反应,以实现自定义应用程序的行为-例如,在会话期满时更新用户记录。

  • 主机地址保留 – Shiro 会话保留启动会话的主机的 IP 地址或主机名。这使您能够确定用户所在的位置并做出相应的反应(在 IP 关联是确定性的 Intranet 环境中通常很有用)。

  • 不活动/过期支持 –会话由于不活动而到期,如预期的那样,但是可以通过touch()方法延长会话的时间,以保持它们“活动”。这在 Rich Internet Application(RIA)环境中非常有用,在该环境中,用户可能正在使用桌面应用程序,但可能不会与服务器定期进行通信,但是服务器会话不应过期。

  • 透明的 Web 使用 -Shiro 的 Web 支持完全实现并支持 Session 的 Servlet 2.5 规范(HttpSession接口及其所有关联的 API)。这意味着您可以在现有的 Web 应用程序中使用 Shiro 会话,而无需更改任何现有的 Web 代码。

  • 可用于 SSO -由于 Shiro 会话基于 POJO,因此可以轻松地存储在任何数据源中,并且可以根据需要在应用程序之间“共享”它们。我们将其称为“穷人的 SSO”,由于共享会话可以保留身份验证状态,因此可以用来提供简单的登录体验。

Using Sessions

与 Shiro 中的几乎所有其他内容一样,您可以通过与当前执行的Subject进行交互来获取Session

Subject currentUser = SecurityUtils.getSubject();

Session session = currentUser.getSession();
session.setAttribute( "someKey", someValue);

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

对于熟悉HttpServletRequest API 的用户,Subject.getSession(boolean create)方法的功能与HttpServletRequest.getSession(boolean create)方法相同:

  • 如果Subject已经具有Session,则布尔参数将被忽略并且Session立即返回

  • 如果Subject还没有Session并且create布尔参数为true,则将创建并返回一个新会话。

  • 如果Subject还没有Session并且create布尔参数为false,则不会创建新的会话,并返回null

Any Application

getSession呼叫可以在任何应用程序中使用,甚至可以在非 Web 应用程序中使用。

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

获取主题的Session后,您可以使用它进行很多操作,例如设置或检索属性,设置其超时等等。请参见Session JavaDoc,以查看单个会话可以执行的操作。

The SessionManager

顾名思义,SessionManager 可以 Management 应用程序中所有主题的会话-创建,删除,不活动和验证等。与 Shiro 中的其他核心体系结构组件一样,SessionManagerSecurityManager维护的顶级组件。

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

Web Applications

Web 应用程序使用不同的SessionManager实现。请参阅Web文档以获取特定于 Web 的会话 Management 信息。

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

在 shiro.ini 中配置新的 SessionManager

[main]
...
sessionManager = com.foo.my.SessionManagerImplementation
securityManager.sessionManager = $sessionManager

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

Session Timeout

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

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

在 shiro.ini 中设置默认会话超时

[main]
...
# 3,600,000 milliseconds = 1 hour
securityManager.sessionManager.globalSessionTimeout = 3600000

Per-Session Timeout

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

Session Listeners

Shiro 支持SessionListener的概念,可让您在重要的会话事件发生时对其做出反应。您可以实现SessionListener接口(或扩展便捷性SessionListenerAdapter)并相应地对会话操作做出反应。

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

shiro.ini 中的 SessionListener 配置

[main]
...
aSessionListener = com.foo.my.SessionListener
anotherSessionListener = com.foo.my.OtherSessionListener

securityManager.sessionManager.sessionListeners = $aSessionListener, $anotherSessionListener, etc.

All Session Events

当任何会话(不仅是特定会话)发生事件时,都会通知SessionListeners

Session Storage

每当创建或更新会话时,其数据都需要保留到存储位置,以便应用程序稍后可以访问它。类似地,当会话无效并被更长时间使用时,需要将其从存储中删除,以免会话数据存储空间耗尽。 SessionManager实现将这些创建/读取/更新/删除(CRUD)操作委派给内部组件SessionDAO,该组件反映了数据访问对象(DAO)设计模式。

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

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

在 shiro.ini 中配置 SessionDAO

[main]
...
sessionDAO = com.foo.my.SessionDAO
securityManager.sessionManager.sessionDAO = $sessionDAO

但是,正如您可能期望的那样,Shiro 已经有了一些不错的SessionDAO实现,您可以根据需要使用这些开箱即用的子类。

Web Applications

上述securityManager.sessionManager.sessionDAO = $sessionDAO分配仅在使用 Shiro 本机会话 Management 器时有效。默认情况下,Web 应用程序不使用本机会话 Management 器,而是保留不支持 SessionDAO 的 Servlet 容器的默认会话 Management 器。如果要在基于 Web 的应用程序中启用 SessionDAO 进行自定义会话存储或会话群集,则必须首先配置本机 Web 会话 Management 器。例如:

[main]
...
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
securityManager.sessionManager = $sessionManager

# Configure a SessionDAO and then set it:
securityManager.sessionManager.sessionDAO = $sessionDAO

Configure a SessionDAO!

Shiro 的默认配置本机 SessionManager 使用“仅在内存中” **会话存储。这不适用于大多数生产应用。大多数生产应用程序将需要配置提供的 EHCache 支持(请参见下文)或提供自己的SessionDAO实现。请注意,默认情况下,Web 应用程序使用基于 Servlet 容器的 SessionManager,并且不会出现此问题。这只是使用 Shiro 本机 SessionManager 时的问题。

EHCache SessionDAO

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

Use EHCache as your default

如果您没有编写自定义SessionDAO,请务必在 Shiro 配置中启用 EHCache。除了会话之外,EHCache 还可以带来好处,还可以缓存身份验证和授权数据。有关更多信息,请参见Caching文档。

Container-Independent Session Clustering

如果快速需要独立于容器的会话群集,EHCache 也是不错的选择。您可以透明地将TerraCotta插入 EHCache 后面,并具有与容器无关的群集会话缓存。无需再担心 Tomcat,JBoss,Jetty,WebSphere 或 WebLogic 特定的会话集群了!

为会话启用 EHCache 非常容易。首先,请确保您的 Classpath 中有shiro-ehcache-<version>.jar文件(请参见Download页或使用 Maven 或 Ant Ivy)。

进入 Classpath 后,第一个shiro.ini示例将向您展示如何使用 EHCache 满足 Shiro 的所有缓存需求(而不仅仅是 Session 支持):

在 shiro.ini 中为 Shiro 的所有缓存需求配置 EHCache

[main]

sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
securityManager.sessionManager.sessionDAO = $sessionDAO

cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
securityManager.cacheManager = $cacheManager

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

然后,当SessionManager要求EnterpriseCacheSessionDAO保留Session时,它将使用 EHCache 支持的Cache实现存储会话数据。

Web Applications

使用 Shiro 本机 SessionManager 实现时,请不要忘记分配SessionDAO是一项功能。默认情况下,Web 应用程序使用基于 Servlet 容器的 SessionManager,它不支持SessionDAO。如果要在 Web 应用程序中使用基于 Ehcache 的会话存储,请将本机 Web SessionManager 配置为explained above

EHCache 会话缓存配置

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

但是,如果您希望更改缓存设置,或配置自己的ehcache.xml或 EHCache net.sf.ehcache.CacheManager实例,则需要配置缓存区域以确保正确处理会话。

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

<cache name="shiro-activeSessionCache"
       maxElementsInMemory="10000"
       overflowToDisk="true"
       eternal="true"
       timeToLiveSeconds="0"
       timeToIdleSeconds="0"
       diskPersistent="true"
       diskExpiryThreadIntervalSeconds="600"/>

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

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

  • eternal="true"-确保缓存条目(会话实例)永远不会被缓存自动过期或删除。这是必需的,因为 Shiro 会根据计划的过程进行自己的验证(请参见下面的“会话验证和计划”)。如果将其关闭,则缓存可能会在 Shiro 不了解的情况下逐出 Sessions,这可能会导致问题。

EHCache 会话缓存名称

默认情况下,EnterpriseCacheSessionDAOCacheManager询问名为“ shiro-activeSessionCache”的Cache。如上所述,预期此缓存名称/区域将在ehcache.xml中配置。

如果要使用其他名称代替该默认名称,则可以在EnterpriseCacheSessionDAO上配置该名称,例如:

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

...
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionDAO.activeSessionsCacheName = myname
...

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

自定义会话 ID

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

默认SessionIdGeneratorJavaUuidSessionIdGenerator,它基于 Java UUIDs生成String ID。此实现适用于所有生产环境。

如果这不能满足您的需求,则可以实现SessionIdGenerator接口并在 Shiro 的SessionDAO实例上配置实现。例如,在shiro.ini中:

在 shiro.ini 中配置 SessionIdGenerator

[main]
...
sessionIdGenerator = com.my.session.SessionIdGenerator
securityManager.sessionManager.sessionDAO.sessionIdGenerator = $sessionIdGenerator

会话验证和计划

必须验证会话,以便可以从会话数据存储中删除任何无效(过期或停止)的会话。这样可以确保数据存储不会随着时间的流逝而填满,这些会话将永远不会再次使用。

出于性能原因,仅验证Sessions以查看它们在被访问时是否已停止或过期(即subject.getSession())。这意味着,如果没有其他常规定期验证,Session个孤儿将开始填充会话数据存储。

一个说明孤儿的常见示例是 Web 浏览器场景:假设用户登录到 Web 应用程序,并创建了一个会话来保留数据(身份验证状态,购物车等)。如果用户在应用程序不知道的情况下没有注销并关闭浏览器,则他们的会话本质上只是在会话数据存储中“四处闲逛”(孤立)。 SessionManager无法检测到用户不再使用其浏览器,并且该会话再也无法访问(它是孤立的)。

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

Default SessionValidationScheduler

在所有环境中可用的默认SessionValidationSchedulerExecutorServiceSessionValidationScheduler,它使用 JDK ScheduledExecutorService来控制验证应多久进行一次。

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

shiro.ini 中的 ExecutorServiceSessionValidationScheduler 间隔

[main]
...
sessionValidationScheduler = org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler
# Default is 3,600,000 millis = 1 hour:
sessionValidationScheduler.interval = 3600000

securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler

Custom SessionValidationScheduler

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

在 shiro.ini 中配置自定义 SessionValidationScheduler

[main]
...
sessionValidationScheduler = com.foo.my.SessionValidationScheduler
securityManager.sessionManager.sessionValidationScheduler = $sessionValidationScheduler

禁用会话验证

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

在 shiro.ini 中禁用会话验证计划

[main]
...
securityManager.sessionManager.sessionValidationSchedulerEnabled = false

从会话数据存储中检索会话时,仍将对其进行验证,但这将禁用 Shiro 的定期验证。

Enable Session Validation somewhere

如果关闭 Shiro 的会话验证计划程序,则必须通过其他某种机制(定时作业等)执行定期的会话验证。这是确保会话孤儿不填满数据存储的唯一方法。

无效的会话删除

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

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

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

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

在 shiro.ini 中禁用无效的会话删除

[main]
...
securityManager.sessionManager.deleteInvalidSessions = false

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

还请注意,即使您阻止 Shiro 删除无效的会话,您仍然应该以某种方式启用会话验证-通过 Shiro 的现有验证机制或通过您自己提供的自定义机制(有关更多信息,请参见上面的“禁用会话验证”部分)。验证机制将更新您的会话记录以反映无效状态(例如,无效状态,上次访问时间等),即使您自己在其他时间手动删除它们也是如此。

Warning

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

还要注意,禁用会话删除与禁用会话验证计划“不一样”。您几乎应该始终使用会话验证调度机制-Shiro 直接支持的一种或您自己的机制。

Session Clustering

关于 Apache Shiro 的会话功能,最令人兴奋的事情之一是您可以在本地群集主题会话,而无需再担心如何根据容器环境对会话进行群集。也就是说,如果您使用 Shiro 的本机会话并配置会话集群,则可以部署到开发中的 Jetty 或 Tomcat,Producing 的 JBoss 或 Geronimo 或任何其他环境-始终不用担心特定于容器/环境的问题群集设置或配置。在 Shiro 中只需配置一次会话群集,无论您的部署环境如何,它都可以正常工作。

那么它是怎样工作的?

由于 Shiro 基于 POJO 的 N 层体系结构,因此启用会话群集就像在会话持久性级别启用群集机制一样简单。也就是说,如果您配置具有群集功能的SessionDAO,则 DAO 可以与群集机制进行交互,而 Shiro 的SessionManager则无需了解群集问题。

Distributed Caches

诸如Ehcache+TerraCottaGigaSpaces Oracle CoherenceMemcached(以及许多其他)之类的分布式缓存已经解决了持久性级别的分布式数据问题。因此,在 Shiro 中启用会话群集就像将 Shiro 配置为使用分布式缓存一样简单。

这使您可以灵活地选择适合您的环境的确切群集机制。

Cache Memory

请注意,当启用分布式/企业缓存作为您的会话群集数据存储时,以下两种情况之一必须为真:

  • 分布式缓存具有足够的群集范围内存来保留所有活动/当前会话

  • 如果分布式缓存没有足够的群集范围内存来保留所有活动会话,则它必须支持磁盘溢出,因此会话不会丢失。

缓存不支持这两种情况中的任何一种都会导致会话随机丢失,这可能使最终用户感到沮丧。

EnterpriseCacheSessionDAO

如您所料,Shiro 已经提供了SessionDAO实现,可将数据持久保存到企业/分布式缓存中。 EnterpriseCacheSessionDAO期望在其上配置 Shiro CacheCacheManager,以便可以利用缓存机制。

例如,在shiro.ini中:

#This implementation would use your preferred distributed caching product's APIs:
activeSessionsCache = my.org.apache.shiro.cache.CacheImplementation

sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionDAO.activeSessionsCache = $activeSessionsCache

securityManager.sessionManager.sessionDAO = $sessionDAO

尽管您可以如上所述将Cache实例直接注入到SessionDAO,但是通常配置通用CacheManager来满足 Shiro 的所有缓存需求(会话以及身份验证和授权数据)通常更为常见。在这种情况下,您无需告诉EnterpriseCacheSessionDAO CacheManager中应用于存储活动会话的缓存名称,而不必直接配置Cache实例。

For example:

# This implementation would use your caching product's APIs:
cacheManager = my.org.apache.shiro.cache.CacheManagerImplementation

# Now configure the EnterpriseCacheSessionDAO and tell it what
# cache in the CacheManager should be used to store active sessions:
sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
# This is the default value.  Change it if your CacheManager configured a different name:
sessionDAO.activeSessionsCacheName = shiro-activeSessionsCache
# Now have the native SessionManager use that DAO:
securityManager.sessionManager.sessionDAO = $sessionDAO

# Configure the above CacheManager on Shiro's SecurityManager
# to use it for all of Shiro's caching needs:
securityManager.cacheManager = $cacheManager

但是上述配置有些奇怪。你注意到了吗?

关于此配置的有趣之处在于,在配置中我们实际上没有告诉sessionDAO实例使用CacheCacheManager!那么sessionDAO如何使用分布式缓存?

当 Shiro 初始化SecurityManager时,它将检查SessionDAO是否实现CacheManagerAware接口。如果是这样,它将自动提供任何可用的全局配置CacheManager

因此,当 Shiro 评估securityManager.cacheManager = $cacheManager行时,它将发现EnterpriseCacheSessionDAO实现了CacheManagerAware接口并以您配置的CacheManager作为方法参数调用setCacheManager方法。

然后在运行时,当EnterpriseCacheSessionDAO需要activeSessionsCache时,它将要求CacheManager实例将其返回,并使用activeSessionsCacheName作为查找键来获取Cache实例。该Cache实例(由您的分布式/企业缓存产品的 API 支持)将用于存储和检索所有SessionDAO CRUD 操作的会话。

Ehcache 秦始皇兵马俑

人们在使用 Shiro 时获得成功的一种分布式缓存解决方案是 Ehcache Terracotta 配对。有关如何使用 Ehcache 启用分布式缓存的完整详细信息,请参阅 Ehcache 托管的兵马俑的分布式缓存文档。

一旦将 Terracotta 集群与 Ehcache 一起使用,针对 Shiro 的部分就非常简单。阅读并遵循Ehcache SessionDAO文档,但我们需要进行一些更改

Ehcache 会话缓存配置referenced previously将不起作用-需要特定于 Terracotta 的配置。这是经过测试可以正常工作的示例配置。将其内容保存在文件中,并保存在ehcache.xml文件中:

TerraCotta 会话群集

<ehcache>
    <terracottaConfig url="localhost:9510"/>
    <diskStore path="java.io.tmpdir/shiro-ehcache"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120">
        <terracotta/>
    </defaultCache>
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           eternal="true"
           timeToLiveSeconds="0"
           timeToIdleSeconds="0"
           diskPersistent="false"
           overflowToDisk="false"
           diskExpiryThreadIntervalSeconds="600">
        <terracotta/>
    </cache>
    <!-- Add more cache entries as desired, for example,
         Realm authc/authz caching: -->
</ehcache>

当然,您将需要更改<terracottaConfig url="localhost:9510"/>条目以引用 Terracotta 服务器阵列的相应主机/端口。还要注意,与previous配置不同,ehcache-activeSessionCache元素 * *将diskPersistentoverflowToDisk属性设置为true。它们都应为false,因为在群集配置中不支持真实值。

保存此ehcache.xml文件后,我们需要在 Shiro 的配置中引用该文件。假设您已在 Classpath 的根目录下访问了特定于赤土的ehcache.xml文件,这是最终的 Shiro 配置,该配置可启用 Terracotta Ehcache 集群以满足 Shiro 的所有需求(包括 Sessions):

使用 Ehcache 和 Terracotta 进行会话群集的 shiro.ini

sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
# This name matches a cache name in ehcache.xml:
sessionDAO.activeSessionsCacheName = shiro-activeSessionsCache
securityManager.sessionManager.sessionDAO = $sessionDAO

# Configure The EhCacheManager:
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile = classpath:ehcache.xml

# Configure the above CacheManager on Shiro's SecurityManager
# to use it for all of Shiro's caching needs:
securityManager.cacheManager = $cacheManager

请记住, Order 事项 。通过最后在securityManager上配置cacheManager,我们确保 CacheManager 可以传播到所有先前配置的CacheManagerAware组件(例如EnterpriseCachingSessionDAO)。

Zookeeper

用户也报告了使用Apache Zookeeper来 Management/协调分布式会话。如果您有任何有关此操作方式的文档/Comment,请将其发布到 Shiro Mailing Lists

会话和主题状态

有状态的应用程序(允许会话)

默认情况下,Shiro 的 SecurityManager 实现将使用主题的会话作为一种策略来存储主题的身份(PrincipalCollection)和身份验证状态(subject.isAuthenticated())以供 continue 参考。这通常在主题登录后或通过 RememberMe 服务发现主题的身份时发生。

这种默认方法有一些好处:

  • 为请求,调用或消息提供服务的任何应用程序都可以将会话 ID 与请求/调用/消息有效负载相关联,而这正是 Shiro 要将用户与入站请求相关联所必需的。例如,如果使用Subject.Builder,这就是获取关联的 Subject 所需的全部:
Serializable sessionId = //get from the inbound request or remote method invocation payload Subject requestSubject = new Subject.Builder().sessionId(sessionId).buildSubject();

这对于大多数 Web 应用程序以及编写远程处理或消息传递框架的任何人都非常方便。 (实际上,这是 Shiro 的 Web 支持如何在其自己的框架代码中将 Subjects 与 ServletRequest 关联的方式)。

  • 首次访问时,在初始请求中找到的任何“ RememberMe”身份都可以保留到会话中。这样可以确保可以在请求中保存主题的 Memory 身份,而无需在每个请求中都对序列进行反序列化和解密。例如,在 Web 应用程序中,如果会话中已知身份,则无需在每个请求上都读取加密的 RememberMe cookie。这可以是很好的性能增强。

尽管上述默认策略对大多数应用程序来说都是很好的(并且通常是理想的),但是在尝试尽可能 Stateless 的应用程序中这不是理想的。许多 Stateless 架构要求在请求之间不能存在任何持久状态,在这种情况下,将不允许会话(会话本质上代表持久状态)。

但是,此要求的代价是方便-不能在请求中保留主题状态。这意味着具有此要求的应用程序必须确保可以针对“每个”请求以其他某种方式表示“主题”状态。

这几乎总是通过验证应用程序处理的每个请求/调用/消息来实现的。例如,大多数 StatelessWeb 应用程序通常通过实施 HTTP Basic 身份验证来支持此功能,从而允许浏览器代表最终用户对每个请求进行身份验证。远程处理或消息传递框架必须确保将主题主体和凭据附加到通常由框架代码执行的每个调用或消息有效负载上。

禁用主题状态会话存储

从 Shiro 1.2 及更高版本开始,希望禁用 Shiro 将主题状态持久化到会话的内部实现策略的应用程序可以通过执行以下操作完全在* all *主题上禁用此方法:

shiro.ini中,在securityManager上配置以下属性:

shiro.ini

[main]
...
securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled = false
...

这将阻止 Shiro 使用主题会话在针对所有主题的请求/调用/消息中存储该主题的状态。只要确保您对每个请求都进行身份验证,Shiro 就可以知道给定请求/调用/消息的主题是谁。

Shiro's Needs vs. Your Needs

这将使 Shiro 自己的实现无法使用 Sessions 作为存储策略。它“不”会完全禁用会话。如果您自己的任何代码显式调用subject.getSession()subject.getSession(true),则仍将创建会话。

混合方法

上面的shiro.ini配置行(securityManager.subjectDAO.sessionStorageEvaluator.sessionStorageEnabled = false)将禁止 Shiro 使用 Session 作为* all *主题的实现策略。

但是,如果您想要一种混合方法呢?如果某些主题应该召开会议而其他主题不应该举行会议怎么办?这种混合方法可能对许多应用程序有益。例如:

  • 也许人类受试者(例如网络浏览器用户)应该能够使用 Sessions 来获得上述好处。

  • 也许非人类主题(例如 APIClient 端或第三方应用程序)不应创建会话,因为它们与软件的交互可能是间歇性的和/或不稳定的。

  • 也许某个类型的所有主题或从某个位置访问系统的那些主题都应在会话中保持状态,而其他所有主题则不应。

如果您需要这种混合方法,则可以实现SessionStorageEvaluator

SessionStorageEvaluator

如果您想精确控制哪些主题可以保持其会话状态不变,可以实现org.apache.shiro.mgt.SessionStorageEvaluator界面,并告诉 Shiro 确切的主题应该支持会话存储。

该接口只有一个方法:

SessionStorageEvaluator

public interface SessionStorageEvaluator {

    public boolean isSessionStorageEnabled(Subject subject);

}

有关 API 的详细说明,请参见SessionStorageEvaluator JavaDoc

您可以实现此接口,并检查主题以了解做出此决定可能需要的任何信息。

Subject Inspection

实施isSessionStorageEnabled(subject)接口方法时,您始终可以查看Subject并访问做出决定所需的任何内容。当然,所有预期的 Subject 方法都可以使用(getPrincipals()等),但是特定于环境的Subject实例也很有价值。

例如,在 Web 应用程序中,如果必须基于当前ServletRequest中的数据做出该决定,则可以获取请求或响应,因为运行时Subject实例实际上是WebSubject实例:

...
    public boolean isSessionStorageEnabled(Subject subject) {
        boolean enabled = false;
        if (WebUtils.isWeb(Subject)) {
            HttpServletRequest request = WebUtils.getHttpRequest(subject);
            //set 'enabled' based on the current request.
        } else {
            //not a web request - maybe a RMI or daemon invocation?
            //set 'enabled' another way...
        }

        return enabled;
    }

N.B. 框架开发人员应牢记这种访问类型,并确保可通过特定于环境的Subject实现访问任何请求/调用/消息上下文对象。如果您需要一些帮助来设置框架/环境,请联系 Shiro 用户邮件列表。

Configuration

实施SessionStorageEvaluator接口后,可以在shiro.ini中对其进行配置:

shiro.ini SessionStorageEvaluator 配置

[main]
...
sessionStorageEvaluator = com.mycompany.shiro.subject.mgt.MySessionStorageEvaluator
securityManager.subjectDAO.sessionStorageEvaluator = $sessionStorageEvaluator

...

Web Applications

通常,Web 应用程序希望根据每个请求简单地启用或禁用会话创建,而不管哪个主体正在执行请求。在支持 REST 和 Messaging/RMI 体系结构时,通常可以很好地使用它。例如,也许普通的最终用户(使用浏览器的人员)被允许创建和使用会话,但是远程 APIClient 端使用 REST 或 SOAP,并且根本不应该拥有会话(因为它们在每个请求中都经过身份验证,这在 Windows 中很常见)。 REST/SOAP 体系结构)。

为了支持此混合/按请求的功能,已将noSessionCreation过滤器添加到 Shiro 的针对 Web 应用程序启用的默认过滤器的“池”中。此过滤器将阻止在请求期间创建新会话,以确保 Stateless 体验。在shiro.ini [urls]部分中,通常会在所有其他过滤器之前定义此过滤器,以确保永远不会使用会话。

For example:

shiro.ini-禁用每个请求的会话创建

[urls]
...
/rest/** = noSessionCreation, authcBasic, ...

此过滤器允许任何现有会话使用会话,但不允许在过滤请求期间创建新会话。也就是说,对请求或主题尚不存在现有会话的以下四个方法调用中的任何一个都会自动触发DisabledSessionException

  • httpServletRequest.getSession()

  • httpServletRequest.getSession(true)

  • subject.getSession()

  • subject.getSession(true)

如果Subject在访问 noSessionCreation-protected-URL 之前已经具有会话,则上述 4 个调用仍将按预期进行。

最后,在所有情况下都将始终允许以下调用:

  • httpServletRequest.getSession(false)

  • subject.getSession(false)

捐赠给 ASF | License

版权所有©2008-2020 The Apache Software Foundation