Hello Mr.J——shiro session管理机制

  上一篇随便的写了些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一样能用,该有的都有了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值