在spring应用中使用shiro框架关于session解析

  • Post author:
  • Post category:其他


先看shiro过滤器的UML

在这里插入图片描述


已经有cookie保存sessionId的请求场景:


1.请求第一次通过shiro的过滤器 OncePerRequestFilter 时,会把request,response包装成ShiroHttpServletRequest和ShiroHttpServletResponse,并且在请求中设置一个属性标识,作用就是当这个请求再次经过过滤器 OncePerRequestFilter 时,不会再包装请求和响应了.

2.请求第一次通过 OncePerRequestFilter 时,会把请求分到 AbstractShiroFilter 过滤器中,看上面的UML图,然后在AbstractShiroFilter中,有下面代码:

final ServletRequest request = this.prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = this.prepareServletResponse(request, servletResponse, chain);
Subject subject = this.createSubject(request, response);
subject.execute(new Callable() {
    public Object call() throws Exception {
        AbstractShiroFilter.this.updateSessionLastAccessTime(request, response);
        AbstractShiroFilter.this.executeChain(request, response, chain);
        return null;
    }
});

上面的代码分析:

第一行是包装servlet规范的request变成ShiroHttpServletRequest

第二行是包装servlet规范的response变成ShiroHttpServletResponse

第三行是创建当前请求的subject(即当前shiro的subject),有sessionId就创建包含sessionId的subject,没有就创建没有包含sessionId的subject

剩下的就是当前的subject执行shiro配置的过滤器链,这些shiro链中的过滤器,都是继承OncePerRequestFilter 的,所以当前的请求会再次通过OncePerRequestFilter ,当这个请求再次通过OncePerRequestFilter 时,已经在请求中设置了一个属性标识,通过判断这个属性标识,不会再次包装请求和响应,即不会再次包装ShiroHttpServletRequest和ShiroHttpServletResponse

3.回归主题,当执行

Subject subject = this.createSubject(request, response);

这句代码跟踪发现,请求会到 DefaultWebSessionManager 类中,就是session的管理器

在这个类中方法

private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
	String id = this.getSessionIdCookieValue(request, response);
	if (id != null) {
	    request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "cookie");
	}

上面代码就是从请求过来的cookie中获取sessionId

shiro可以配置自己的cookie模版,把这个模版返回给浏览器,然后当请求过来时,根据这cookie模版的名字获取值sessionId

获取sessionId后,就在请求中设置属性,说请求中的sessionId,是从cookie中获取的,不是新创建的

请求再次设置如下两个属性,说是shiro请求的sessionId,和这个sessionId是有效的

if (id != null) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
}

4.从cookie中获取了sessionId后,shiro是怎么获取保存的session的?

注意:和session相关的操作,要找shiro中session管理器,因为session管理器是管理session创建,获取,删除的,所以一定要有这样的意识

在DefaultWebSessionManager 的父类中extends DefaultSessionManager

发现如下代码

Session s = this.retrieveSessionFromDataSource(sessionId);
protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
	return this.sessionDAO.readSession(sessionId);
}

可知是找sessionDAO,根据sessionId获取session对象的

sessionDAO是session访问对象,将session保存在缓存中,而session所在的缓存可以是redis或其他

5.shiro框架会先使用sessionDAO根据sessionId获取session,后再调用sessionDAO的create(session)方法,再次保存到缓存中,即相当于更新了session的有效期,访问一次就更新一次

public interface SessionDAO {
    Serializable create(Session var1);

    Session readSession(Serializable var1) throws UnknownSessionException;

    void update(Session var1) throws UnknownSessionException;

    void delete(Session var1);

    Collection<Session> getActiveSessions();
}


一开始访问应用,还没有创建session场景


1.一开始访问shiro框架时,没有cookie,没有sessionId

shiro框架也会看有没有cookie,有没有sessionId

注意:补充重要知识

在请求第一次通过 OncePerRequestFilter 时,会把请求分到 AbstractShiroFilter 过滤器中,

进入

final ServletRequest request = this.prepareServletRequest(servletRequest, servletResponse, chain);
final ServletResponse response = this.prepareServletResponse(request, servletResponse, chain);
Subject subject = this.createSubject(request, response);
subject.execute(new Callable() {
    public Object call() throws Exception {
        AbstractShiroFilter.this.updateSessionLastAccessTime(request, response);
        AbstractShiroFilter.this.executeChain(request, response, chain);
        return null;
    }
});

上面代码除包装request,response和创建subject外,还更新session最新访问时间(如果有)

开始执行shiro中的过滤器链

AbstractShiroFilter.this.executeChain(request, response, chain);

上面这句代码是shiro过滤器链执行的开始,具体怎么做?看代码

protected void executeChain(ServletRequest request, ServletResponse response, FilterChain origChain) throws IOException, ServletException {
   FilterChain chain = this.getExecutionChain(request, response, origChain);
   chain.doFilter(request, response);
}

看this.getExecutionChain

protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
    FilterChain chain = origChain;
    FilterChainResolver resolver = this.getFilterChainResolver();
    if (resolver == null) {
        log.debug("No FilterChainResolver configured.  Returning original FilterChain.");
        return origChain;
    } else {
        FilterChain resolved = resolver.getChain(request, response, origChain);
        if (resolved != null) {
            log.trace("Resolved a configured FilterChain for the current request.");
            chain = resolved;
        } else {
            log.trace("No FilterChain configured for the current request.  Using the default.");
        }

        return chain;
    }
}

上面代码就是获取FilterChainResolver ,这个类在shiro框架启动时就把shiro配置的所有属于shiro的每个路径对应的过滤器链集中存放起来,再把tomcat的过滤器链保存shiro过滤器链resolved中,等执行完shiro的过滤器链后,再执行tomcat过滤器链

shiro的过滤器链是 ProxiedFilterChain

ProxiedFilterChain implements FilterChain

2.回归正题,当前请求通过当前请求路径对应的shiro过滤器链集中的一个链后

就到了servlet,即springmvc的dispatcherServlet

在接下来执行中

最终是javax.servlet.http.HttpServletRequestWrapper包装类中,调用ShiroHttpServletRequest中的

在通过shiro过滤器时,就包装了request为ShiroHttpServletRequest

public HttpSession getSession() {
        return this.getSession(true);
}
Session shiroSession = this.getSubject().getSession(create);//由shiro的subject创建
this.session = new ShiroHttpSession(shiroSession, this, this.servletContext);

ShiroHttpSession 是继承 servlet规范的 HttpSession 的,这里将shiro的Session包装,

shiro最后调用SessionDAO保存session

这样传到controller层的时候,使用的就是shiro的Session



版权声明:本文为weixin_41490593原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。