3-Shiro的会话管理

        会话保持必须要前后端的配合,前端需要传递一个会话的唯一标识,后端才能取出会话。这个规则在任何场景、任何应用中都是一样的。

        在web应用中,前端的这个唯一标识可以是cookie,这样的话,很多人会忽略这一点,这是非常不应该的,这样的话,前端在会话上不需要写代码,直接依赖浏览器的cookie能力即可。在非web应用中,没有浏览器的cookie能力,就需要前端自行解决会话唯一标识的保存和传递。

        在shiro中也是一样,使用在web应用中时,简单架构上可能不需要我们管会话唯一标识,但你不能忽略cookie的存在。对于web应用而言,会话接口通常封装在Request中,以Tomcat为例,HttpServletRequest接口就定义了getSession()方法,用于获取创建会话。

        在shiro中,有一个ShiroHttpServletRequest,它继承了HttpServletRequestWrapper,对于HttpServletRequestWrapper我们必须要了解,否则就很难解释后面的内容了。javax.servlet.ServletRequest这个接口是tomcat对Servlet标准中request的顶级接口定义,javax.servlet.http.HttpServletRequest接口继承了ServletRequest接口,javax.servlet.ServletRequestWrapper类实现了接口ServletRequest,javax.servlet.http.HttpServletRequestWrapper继承了ServletRequestWrapper且实现了HttpServletRequest。这种类层次设计思路很常见,一定要学会。顶层接口有一个实现类,顶层接口也会有第二层接口,第二层接口的实现类往往会继承顶层接口的实现类,这样就不用把顶层接口里的接口方法全部重新实现一遍。

      HttpServletRequestWrapper是一个功能齐全的Servlet请求类,所以如果你的框架需要重新定义ServletReqeust的行为,那么继承HttpServletRequestWrapper是一个非常正确的决定,因为很多框架就是这个做的。shiro也不例外。在shiro中,有一个org.apache.shiro.web.servlet.ShiroHttpServletRequest:

/**
 * A {@code ShiroHttpServletRequest} wraps the Servlet container's original {@code ServletRequest} instance, but ensures
 * that all {@link HttpServletRequest} invocations that require Shiro's support ({@link #getRemoteUser getRemoteUser},
 * {@link #getSession getSession}, etc) can be executed first by Shiro as necessary before allowing the underlying
 * Servlet container instance's method to be invoked.
 *
 * @since 0.2
 */
public class ShiroHttpServletRequest extends HttpServletRequestWrapper {
...
}

shiro对此的解释是:ShiroHttpServletRequest包装了Servlet容器的原始请求实例,但是保证了需要shiro支持的方法能在容器实例方法调用之前被调用,如:getRemoteUser(),getSession()等。
看到这里,就应该知道了,shiro会插手原生的会话方法。下面我们就来看看shiro会话管理的逻辑。

        在shiro中,可以使用原生的session,也可以使用shiro的session,取决于ShiroHttpServletRequest#getSession()方法:

public class ShiroHttpServletRequest extends HttpServletRequestWrapper {

    public HttpSession getSession(boolean create) {

        HttpSession httpSession;

        if (isHttpSessions()) {
            httpSession = super.getSession(false);
            if (httpSession == null && create) {
                //Shiro 1.2: assert that creation is enabled (SHIRO-266):
                if (WebUtils._isSessionCreationEnabled(this)) {
                    httpSession = super.getSession(create);
                } else {
                    throw newNoSessionCreationException();
                }
            }
        } else {
            boolean existing = getSubject().getSession(false) != null;
            
            if (this.session == null || !existing) {
                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);
                    }
                } else if (this.session != null) {
                    this.session = null;
                }
            }
            httpSession = this.session;
        }

        return httpSession;
    }

    public HttpSession getSession() {
        return getSession(true);
    }
}

需要说明的是,使用shiro的session时,session是存储到subject中的,其中的关键判断是isHttpSessions(),这个方法追溯到最上面是:org.apache.shiro.web.mgt.DefaultWebSecurityManager#isHttpSessionMode()

public class DefaultWebSecurityManager extends DefaultSecurityManager implements WebSecurityManager {
    public boolean isHttpSessionMode() {
        SessionManager sessionManager = getSessionManager();
        return sessionManager instanceof WebSessionManager && ((WebSessionManager)sessionManager).isServletContainerSessions();
    }
}

在web环境中我们使用的SessionManager通常是WebSessionManager的子类,所以第一个条件通常是满足的,第二个条件取决于具体的子类

public class DefaultWebSessionManager extends DefaultSessionManager implements WebSessionManager {
    /**
     * This is a native session manager implementation, so this method returns {@code false} always.
     *
     * @return {@code false} always
     * @since 1.2
     */
    public boolean isServletContainerSessions() {
        return false;
    }
}
public class ServletContainerSessionManager implements WebSessionManager {
    /**
     * This implementation always delegates to the servlet container for sessions, so this method returns
     * {@code true} always.
     *
     * @return {@code true} always
     * @since 1.2
     */
	public boolean isServletContainerSessions() {
		return true;
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值