shiro源码解析

spring mvc下shiro的session,request等问题  HttpShiroRequest 接管session

最近的一个项目使用的是spring mvc,权限框架使用的是shiro.

不过有一个问题一直困扰着我,现在的session到底是谁的session,是servlet的还是shiro的.

于是我把spring controller参数里面的HttpServletRequest对象和HttpSession对象打印了出来

这两个对象打印的结果是org.apache.shiro.web.servlet.ShiroHttpServletRequest和org.apache.shiro.web.servlet.ShiroHttpSession

在不使用shiro时这些对象应该均为tomcat所实现的类,这说明在shiro执行filter时将request对象包装成了shiro实现的类.

那么shiro是在什么时候将request对象包装了的呢?

prepareServletRequest和prepareServletResponse都是对上层传入的参数ServletRequest和ServletResponse进行再包装。

prepareServletRequest获取SecurityManager和sessionManager的实例

 protected boolean isHttpSessions() {
        return getSecurityManager().isHttpSessionMode();
    }

我们看到OncePerRequestFilter里面实现了Servlet规范的doFilter(),并且将该方法声明为final,可以看出shiro不允许其子类再复写该方法.

但OncePerRequestFilter并没有实现 doFilterInternal(request, response, filterChain),这说明该方法是需要在子类中实现的.

再来看一下OncePerRequestFilter的子类AbstractShiroFilter:

protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain) throws ServletException, IOException {

        Throwable t = null;

        try {

            // 返回被 Shiro 包装过的 Request 与 Response 对象

            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 {

                    // 更新 Session 的最后访问时间

                    updateSessionLastAccessTime(request, response);

                    // 执行 Shiro 的 Filter Chain

                    executeChain(request, response, 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;

            }

            throw new ServletException(t);

        }

    }

果然,在子类AbstractShiroFilter中实现了doFilterInternal()方法.

其中用于包装request的函数:

protected ServletRequest prepareServletRequest(ServletRequest request, ServletResponse response, FilterChain chain) {
        ServletRequest toUse = request;
        if(request instanceof HttpServletRequest) {
            HttpServletRequest http = (HttpServletRequest)request;
            toUse = this.wrapServletRequest(http);
        }

        return toUse;
    }
protected ServletRequest wrapServletRequest(HttpServletRequest orig) {
        return new ShiroHttpServletRequest(orig, this.getServletContext(), this.isHttpSessions());
    }

现在终于知道shiro是怎么把request对象包装成ShiroHttpServletRequest类型的了.

一开始session的困惑也能解开了:

因为session是通过request获取的,所以先看一下ShiroHttpServletRequest获取session的源码:

public HttpSession getSession(boolean create) {
        HttpSession httpSession;
        if(this.isHttpSessions()) {
            httpSession = super.getSession(false);
            if(httpSession == null && create) {
                if(!WebUtils._isSessionCreationEnabled(this)) {
                    throw this.newNoSessionCreationException();
                }

                httpSession = super.getSession(create);
            }
        } else {
            if(this.session == null) {
                boolean existing = this.getSubject().getSession(false) != null;
                Session shiroSession = this.getSubject().getSession(create);
                if(shiroSession != null) {
                    this.session = new ShiroHttpSession(shiroSession, this, this.servletContext);
                    if(!existing) {
                        this.setAttribute(REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
                    }
                }
            }

            httpSession = this.session;
        }

        return httpSession;
    }

这里主要讲的是:

如果this.isHttpSessions()返回true,则返回父类HttpServletRequestWrapper的

也就是servelet规范的session,否则返回ShiroHttpSession对象.

那么this.isHttpSessions()是什么呢?

 
protected boolean httpSessions = true;
public boolean isHttpSessions() {
        return this.httpSessions;
    }

this.isHttpSessions()返回ShiroHttpServletRequest对象的一个属性值,默认是true.

ShiroHttpServletRequest的构造函数是这样的:

public ShiroHttpServletRequest(HttpServletRequest wrapped, ServletContext servletContext, boolean httpSessions) {
        super(wrapped);
        this.servletContext = servletContext;
        this.httpSessions = httpSessions;
    }

这说明在ShiroHttpServletRequest对象生成之初就必须指定httpSessions的值.

再回到刚才shiro包装request的地方.

protected ServletRequest wrapServletRequest(HttpServletRequest orig) {
        return new ShiroHttpServletRequest(orig, this.getServletContext(), this.isHttpSessions());
    }
protected boolean isHttpSessions() {
        return this.getSecurityManager().isHttpSessionMode();
    }

这里的this.isHttpSessions()取决于this.getSecurityManager().isHttpSessionMode()的值.

我们项目里SecurityManager设置为DefaultWebSecurityManager.其中isHttpSessionMode()方法为:

public boolean isHttpSessionMode() {
        SessionManager sessionManager = this.getSessionManager();
        return sessionManager instanceof WebSessionManager && ((WebSessionManager)sessionManager).isServletContainerSessions();
    }

我们这个项目使用的sessionManager是DefaultWebSessionManager,DefaultWebSessionManager实现了sessionManager 接口.

但是DefaultWebSessionManager中该方法返回的是false.

public boolean isServletContainerSessions() {
        return false;
    }

所以最终session得到的是ShiroHttpSession.

转载于:https://my.oschina.net/u/2335171/blog/1536181

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值