上一篇随便的写了些shiro的session管理的配置,和与redis实现session共享
一个完整的企业级Session管理方案,可以为任意的应用提供session支持,甚至不需要Web容器或者EJB容器。
那么我们这次来看看,到底shiro做了什么,实现了可以管理session。
首先,shiro的功能是基于拦截器的,shiroFilter是整个shiro的入口,我们来看一下这里做了什么。
public class ShiroFilter extends AbstractShiroFilter {
@Override
public void init() throws Exception {
WebEnvironment env = WebUtils.getRequiredWebEnvironment(getServletContext());
setSecurityManager(env.getWebSecurityManager());
FilterChainResolver resolver = env.getFilterChainResolver();
if (resolver != null) {
setFilterChainResolver(resolver);
}
}
}
可以看到,shiroFilter这里只有一个初始化的方法。这个方法里面初始化了shiro运行需要的一些东西。
WebEnvironment用于 Web 环境的 SecurityManager 对象,通过读取 shiro.ini 中 [main] 片段生成的,我们可以通过 SecurityUtils.getSecurityManager 方法获取该对象。
FilterChainResolver是 shiro.ini 中 [urls] 片段所配置的 Filter Chain 的解析器,可对一个 URL 配置一个或多个 Filter(用逗号分隔),Shiro 也为我们提供了几个默认的 Filter。
看来这里并没有我们想要的东西,这次主要是找找session相关的内容,我们来看一下父类。
在父类AbstractShiroFilter中,我们找到了doFilterInternal这个方法,其中有我们需要的信息。
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
throws ServletException, IOException {
Throwable t = null;
// 返回被 Shiro 包装过的 Request 与 Response 对象
try {
final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
// 创建 Shiro 的 Subject 对象
final Subject subject = createSubject(request, response);
// 使用异步的方式执行相关操作
subject.execute(new Callable() {
public Object call() throws Exception {
updateSessionLastAccessTime(request, response); // 更新 Session 的最后访问时间
executeChain(request, response, chain); // 执行 Shiro 的 Filter Chain
return null;
}
});
} catch (ExecutionException ex) {
t = ex.getCause();
} catch (Throwable throwable) {
t = throwable;
}
if (t != null) {
if (t instanceof ServletException) {
throw (ServletException) t;
}
if (t instanceof IOException) {
throw (IOException) t;
}
String msg = "Filtered request failed.";
throw new ServletException(msg, t);
}
}
我们需要session的时候就是从request中取出Session,在这我们找到了包装request的代码,下面我们看一下这个包装都做了什么。
protected ServletRequest prepareServletRequest(ServletRequest request, ServletResponse response, FilterChain chain) {
ServletRequest toUse = request;
if (request instanceof HttpServletRequest) {
HttpServletRequest http = (HttpServletRequest) request;
toUse = wrapServletRequest(http);
}
return toUse;
}
恩。。。好像还有一层,这个wrapServletRequest方法。
protected ServletRequest wrapServletRequest(HttpServletRequest orig) {
return new ShiroHttpServletRequest(orig, getServletContext(), isHttpSessions());
}
到
这里就算是到头了,直接返回了一个经过shiro包装诸侯的request对象,getSession的代码我们也顺便看一下吧。
public HttpSession getSession(boolean create) {
HttpSession httpSession;
if (isHttpSessions()) {
httpSession = super.getSession(false);
if (httpSession == null && create) {
//isHttpSessions()为true,则返回父类HttpServletRequestWrapper的,也就是servelet规范的session
if (WebUtils._isSessionCreationEnabled(this)) {
httpSession = super.getSession(create);
} else {
throw newNoSessionCreationException();
}
}
} else {
if (this.session == null) {
//否则返回一个ShiroHttpSession对象.
boolean existing = getSubject().getSession(false) != null;
Session shiroSession = getSubject().getSession(create);
if (shiroSession != null) {
this.session = new ShiroHttpSession(shiroSession, this, this.servletContext);
if (!existing) {
setAttribute(REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
}
}
}
httpSession = this.session;
}
return httpSession;
}
在上面包装request的时候,我们在new一个ShiroHttpServletRequest对象的时候传了这个isHttpSession()的值,我们看一下。
protected boolean isHttpSessions() {
return getSecurityManager().isHttpSessionMode();
}
可以看出来这里也是拿的SecurityManager的IsHttpSessionMode的方法的值。我们在之前几篇博客使用的SecurityManager是默认的DefaultWebSecurityManager,继续找一下isHttpSessionMode这个方法。
public boolean isHttpSessionMode() {
SessionManager sessionManager = getSessionManager();
return sessionManager instanceof WebSessionManager && ((WebSessionManager)sessionManager).isServletContainerSessions();
}
好的,下面就是SessionManager中的方法了,我们上篇博客中使用的是DefaultWebSessionManager,在里面找到isServletContainerSessions。
public boolean isServletContainerSessions() {
return false;
}
费这么大劲返回了个false。。。
好吧,实际上并不是这样的,应为DefaultWebSessionManager本身被设计为一个本地的session管理器,所以一直返回的false。当我们需要servlet规范的session时,我们需要使用其他的session管理器,当然其实shiro的session一样能用,该有的都有了。