Shiro提供了完整的企业级会话管理功能,不必将应用程序耦合到Web
或ejb
技术,提供了会话管理,会话事件监听,会话存储/持久化,容器无关的集群,失效/过期支持,对Web的透明支持,SSO单点登录的支持等特性。即直接使用 Shiro 的会话管理可以直接替换如 Web 容器的会话管理。
前言
shiro 提供了 session 用于保存与用户相关的信息,需要注意到这和 HTTP 的 session 概念是不一样的,它可以运行在没有web环境下。本篇文章会先介绍 Web 环境下的原理,读者有兴趣的话,可以看看在非 Web 环境下的原理。
Session 接口
session
是与单个subject
(用户,守护进程等)相关联的有状态数据上下文,该subject
在一段时间内与软件系统进行交互。session旨
在由业务层进行管理,并且可以通过其他层进行访问,而不受任何给定的客户端技术的束缚。这对Java 系统来说是巨大的好处,因为到目前为止,唯一可行的会话机制是javax.servlet.http.HttpSession
或有状态session EJB
,然而如果使用本session则不必将应用程序耦合到Web
或ejb
技术
Session
定义了主要接口,支持设置时间有效期,存储等。
1 | public interface Session { |
HttpServletSession
实现了 http
的 session
,运行在web
环境。完全由标准的servlet
容器支持HttpSession,它不与Shiro
与会话相关的任何组件 SessionManager,SecurityManager
等进行交互,而是通过与提供的servlet
容器HttpSession HttpSession
实例进行交互来满足所有方法实现。
DelegatingSession
支持本地管理 session,运行在非 web 环境。DelegatingSession
是服务器端的客户端层表示。此实现基本上是服务器端NativeSessionManager NativeSessionManager
的代理,该代理将为每个方法调用返回正确的结果。DelegatingSessio
n 将在适当的时候缓存数据,以避免远程方法调用,仅在必要时与服务器通信。当然,如果将其与NativeSessionManager
业务POJO
一起在进程中使用,则在基于Web的应用程序中可能会出现这种情况,其中基于Web的类和服务器端业务Pojo存在于同一个JVM,将不会进行远程方法调用。
ProxiedSession
只是Session
的代理类。简单的session实现,可立即将所有相应的调用委派给基础代理会话实例*此类对于框架子类最有用,它可以拦截某些 Session调用并执行其他逻辑。
SimpleSession
只是简单的Session
实现类。
Session 管理
session manager分为两类,非 Web 环境和 Web 环境。它负责 session 的创建,查找,删除等管理操作,由SessionManager
接口表示。
SessionManager 接口
SessionManager
只有两个方法,创建和查找 session。通过这两个方法的声明,可以看到多个概念的联系。
1 | public interface SessionManager { |
上述方法涉及到了 Session
,SessionContext
,SessionKey
和SessionManager
四种概念,这里先简单介绍下它们,
Session
存储了用户的相关信息SessionContext
用作初始化Session
实例SessionKey
是Session
实例的 id,用于查找SessionManager
管理着多个Session
SessionManager 类图
session manager
根据使用环境分为 HTTP
和 非 HTTP
两类。下图中左边的部分是使用本地 session
管理,在 Web 环境和非Web
环境下都可以试用。右边的是使用Servlet
自带的 session 管理,只能在Web
环境下使用。
SecurityManager
在单个应用程序中为subject
执行所有安全性操作。 该接口本身主要是为了方便起见-它扩展了org.apache.shiro.authc.Authenticator,Authorizer和SessionManager
接口。对于大多数Shiro
用法,这简化了配置,并且相比单独引用Authenticator, Authorizer, SessionManager
实例更方便;除了上述三个接口之外,此接口还提供了许多支持subject
行为的方法。org.apache.shiro.subject.Subject
对单个用户执行身份验证,授权和会话操作,因此只能由SecurityManager
管理,它了解所有三个功能。另一方面,三个父接口不会“了解”subject
,以确保清晰地分离关注点。实际上,绝大多数应用程序程序员根本不会经常与SecurityManager
进行交互。大多数应用程序程序员仅关心当前正在执行的用户的安全操作,通常通过调用 org.apache.shiro.SecurityUtils.getSubject()
来实现。
WebSercurityManager
此接口表示可以在启用Web的应用程序中使用的SecurityManager
1 | public DefaultWebSecurityManager() { |
ValidatingSessionManager
是可以主动验证可能已过期的会话的SessionManager。NativeSessionManager
定义了本地 session
管理的接口,也就是说它可以存在非 web 环境下。AbstractSessionManager
接口的基本抽象类,支持配置应用程序范围的getGlobalSessionTimeout()、 globalSessionTimeout()
。默认的全局会话超时为30
分钟。WebSessionManager
定义了web session 管理的接口,也就是说它可以存在 web 环境下。
DefaultSessionManager
是用于非 Web 环境下, 默认的管理器。(里面设置了SessionFactory,SessionDAO,CacheManager
)DefaultWebSessionManager
是 shrio 支持用于 Web 环境下,只不过使用了自己的 session 管理。(里面设置了自定义的Cookie)ServletContainerSessionManager
是基于 spring web 实现的,只能用于 Web 环境下,它只是简单的封装了Serlvet
相关功能。(session,cookie完全交由shiro管理)
Web 环境
因为 Web 的场景会非常普遍,而且实现也比较简单,所以会先讲解这部分。ServletContainerSessionManager
类实现了SessionManager
接口,下面是它的代码
1 | public class ServletContainerSessionManager implements WebSessionManager { |
可以看到,它只是简单的调用了HttpServletRequest
相关的方法,创建了HttpServletSession
实例。HttpServletSession
也只是简单的包装了一下HTTPSession
。
1 | public class HttpServletSession implements Session { |
非 Web 环境
DefaultSessionManager
是非 Web 环境下默认的 session 管理器。我们来看看它的实现原理,因为它继承了AbstractNativeSessionManager
类,所以真正的创建 session 实例由doCreateSession
方法负责。从下面的代码可以看到,session的创建是有工厂类SessionFactory
负责,并且创建后还会将其持久化。
1 | public class DefaultSessionManager extends AbstractValidatingSessionManager { |
同样根据 sessionId 查找 session 的方法,最终由retrieveSession
实现。可以看到session的查找最终是由SessionDAO
负责。
1 | public class DefaultSessionManager extends AbstractValidatingSessionManager { |
Session 实例化
上面涉及到了SessionFactory
工厂类,用作创建 session。接下来就讲讲它的原理。
SessionFactory 工厂类
一个简单的工厂类,用于实例化具体的Session
实例。这主要是一种机制,允许实例在需要与默认值不同时在运行时创建实例。框架的最终用户不使用它,而是配置Shiro以在应用程序中工作的用户,通常将其注入org.apache.shiro.mgt.SecurityManager
或SessionManager
。
SessionFactory
接口定义了创建 Session 的方法createSession
,其中参数SessionContext
是用作初始化的。
1 | public interface SessionFactory { |
目前只有SimpleSessionFactory
类实现了该接口,它的实现非常简单
1 | public class SimpleSessionFactory implements SessionFactory { |
初始化参数SessionContext
SessionContext
的相关类图如下所示,虽然比较多,但是原理很简单。
其中RequestPairSource 是提供当前执行请求关联的ServletRequest,ServletResponse
对。 用于框架开发支持,最终用户很少使用。
SessionContext继承
Map`接口,并且定义了 host 和 sessionId 两个属性的操作方法。
1 | public interface SessionContext extends Map<String, Object> { |
DefaultSessionContext
实现了默认的SessionContext
接口。
DefaultWebSessionContext
在它基础之上,添加了获取ServletRequest
和ServletResponse
实例的方法。
Session 持久化
Easy Custom Session Storage - 因为Shiro 的Session 对象是基于 POJO 的,会话数据可以很容易地存储在任意数量的数据源。这允许你自定义你的应用程序会话数据的确切位置——例如,文件系统,联网的分布式缓存,关系数据库,或专有的数据存储。
每当一个会话被创建或更新时,它的数据需要持久化到一个存储位置以便它能够被稍后的应用程序访问。同样地,当一个会话失效且不再被使用时,它需要从存储中删除以便会话数据存储空间不会被耗尽。SessionManager 实现委托这些 Create/Read/Update/Delete(CRUD) 操作为内部组件,同时,SessionDAO,反映了数据访问对象(DAO)设计模式。
SessionDAO 的权力是你能够实现该接口来与你想要的任何数据存储进行通信。这意味着你的会话数据可以驻留在内存中,文件系统,关系数据库或NoSQL 的数据存储,或其他任何你需要的位置。你得控制持久性行为。
SessionDAO
定义了 session 增删改查接口,如下所示:
1 | public interface SessionDAO { |
它的子类图如下所示:
AbstractSessionDAO
实现了SessionDAO
接口,并且定义了doCreate
接口,子类需要实现它并且返回 SessionId
。MemorySessionDAO
使用了 ConcurrentMap
来保存 session。CachingSessionDAO
实现了session缓存,用户可以集成CacheManager
实现自定义的缓存。
当持久化化 session 的时候,SessionDAO
会使用SessionIdGenerator
接口生成唯一的 SessionId
。
1 |
|
Session 集成
我们再回到DefaultSecurityManager
类,看看它是如何集成Session
的。SessionsSecurityManager
是它的父类,它默认使用DefaultSessionManager
实现,实现了 session 管理。
1 | public abstract class SessionsSecurityManager extends AuthorizingSecurityManager { |
Spring Session 集成
session
在不同场景下有两种方式在分布式环境下Session一般会被持久化到应用可访问的外部存储上,供其它程序访问;在单应用程序上一般存储在服务器端。
单应用程序
1 |
|
分布式环境
可以session持久化,例如保存在redis中
1 |
|
注意:
rememberMe功能其实本身使用了session、cookie。所以如果使用了rememberMe就无需设置securityManager.setSessionManager(sessionManager)
,DefaultWebSecurityManager
默认使用了ServletContainerSessionManager
RememberMeManager
负责在主题与应用程序的会话中记住subject
的身份
1 | public CookieRememberMeManager() { |
参考
1 | https://blog.csdn.net/qq_34021712/article/details/80418112 |
- 本文作者: 初心
- 本文链接: http://funzzz.fun/2021/02/03/Apache Shiro --- 8 session/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!