最近的一个项目使用的是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对象包装了的呢?

先看一下shiro的拦截器类图:

ShiroFilter 是整个 Shiro 的入口点,用于拦截需要安全控制的请求进行处理。ShiroFilter 继承自AbstractShiroFilter,而AbstractShiroFilter继承自OncePerRequestFilter

先看一下OncePerRequestFilter的源码 :

public abstract class OncePerRequestFilter extends NameableFilter {

    // 已过滤属性的后缀名

    public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";

    // 是否开启过滤功能

    private boolean enabled = true;

    public boolean isEnabled() {

        return enabled;

    }

    public void setEnabled(boolean enabled) {

        this.enabled = enabled;

    }

    public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        // 获取 Filter 已过滤的属性名

        String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();

        // 判断是否已过滤

        if (request.getAttribute(alreadyFilteredAttributeName) != null) {

            // 若已过滤,则进入 FilterChain 中下一个 Filter

            filterChain.doFilter(request, response);

        } else {

            // 若未过滤,则判断是否未开启过滤功能(其中 shouldNotFilter 方法将被废弃,由 isEnabled 方法取代)

            if (!isEnabled(request, response) || shouldNotFilter(request)) {

                // 若未开启,则进入 FilterChain 中下一个 Filter

                filterChain.doFilter(request, response);

            } else {

                // 若已开启,则将已过滤属性设置为 true(只要保证 Request 中有这个属性即可)

                request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);

                try {

                    // 在子类中执行具体的过滤操作

                    doFilterInternal(request, response, filterChain);

                } finally {

                    // 当前 Filter 执行结束需移除 Request 中的已过滤属性

                    request.removeAttribute(alreadyFilteredAttributeName);

                }

            }

        }

    }

    protected String getAlreadyFilteredAttributeName() {

        String name = getName();

        if (name == null) {

            name = getClass().getName();

        }

        return name + ALREADY_FILTERED_SUFFIX;

    }

 protected boolean isEnabled(ServletRequest request, ServletResponse response) throws ServletException, IOException {

        return isEnabled();

    }

 protected boolean shouldNotFilter(ServletRequest request) throws ServletException {

        return false;

    }

    protected abstract void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException;

}

我们看到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.

转载请注明出处http://www.cnblogs.com/vinozly/p/5080692.html

spring mvc下shiro的session,request等问题的更多相关文章

  1. 理解Spring MVC Model Attribute和Session Attribute

    作为一名 Java Web 应用开发者,你已经快速学习了 request(HttpServletRequest)和 session(HttpSession)作用域.在设计和构建 Java Web 应用 ...

  2. 【译】理解Spring MVC Model Attribute 和 Session Attribute

    作为一名 Java Web 应用开发者,你已经快速学习了 request(HttpServletRequest)和 session(HttpSession)作用域.在设计和构建 Java Web 应用 ...

  3. 基于Spring + Spring MVC + Mybatis + shiro 高性能web构建

    一直想写这篇文章,前段时间 痴迷于JavaScript.NodeJs.AngularJS,做了大量的研究,对前后端交互有了更深层次的认识. 今天抽个时间写这篇文章,我有预感,这将是一篇很详细的文章,详 ...

  4. spring mvc DispatcherServlet详解之一--request通过HandlerMaping获取控制器Controller过程

    整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的第一步:获取控制器. HandlerMapping HandlerMappi ...

  5. 基于Vue+Spring MVC+MyBatis+Shiro+Dubbo开发的分布式后台管理系统

    本文项目代码: 服务端:https://github.com/lining90567/dubbo-demo-server 前端:https://github.com/lining90567/dubbo ...

  6. Spring MVC下拉选项(Select)

    以下示例显示如何在使用Spring Web MVC框架的表单中使用下拉选项(Dropdown).首先使用Eclipse IDE来创建一个WEB工程,实现一个让用户可选择自己所在的国家的功能.并按照以下 ...

  7. spring mvc下实现通过邮箱找回密码功能

    1功能分析 通过spring mvc框架实现通过邮箱找回密码. 2 实现分析 主要是借助某个邮箱的pop3/smtp服务实现的邮件代发功能. 3 源码分析 3.1首先在用户表对应的javabean中加 ...

  8. spring mvc DispatcherServlet详解之三---request通过ModelAndView中获取View实例的过程

    整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet第二步:通过request从Controller获取ModelAndView.现在来讲解第三步:reques ...

  9. spring mvc mybatis shiro构建cms系统ios android

    开发语言: java.ios.android 部署平台: linux.window jdk版本:JDK1.7以上版本 开发工具: eclipse.idea等 服务器中间件:Tomcat 6.7.Jbo ...

随机推荐

  1. Java 之Integer相等比较

    1.问题提出 今天在和同事讨论问题的时候,无意间谈到了Integer对象的比较,先看下代码: package test; public class IntegerEqual { /** * @para ...

  2. Invent 2014回顾

    AWS re:Invent 2014回顾   亚马逊在2014年11月11-14日的拉斯维加斯举行了一年一度的re:Invent大会.在今年的大会上,亚马逊一股脑发布和更新了很多服务.现在就由我来带领 ...

  3. 使用OpenWrt的SDK

    原文:http://wiki.openwrt.org/doc/howto/obtain.firmware.sdk 为什么要使用SDK: Reasons for using the SDK are: C ...

  4. 使用Windows2003创建FTP服务器 - 进阶者系列 - 学习者系列文章

    现在有不少的FTP建设软件,比如Server-U软件.不过本文只介绍使用Windows2003来创建FTP服务器. 1.  打开控制面板的添加删除程序. 2.  打开 添加删除Windows组件 3. ...

  5. IE6/7中li浮动外边距无法撑开ul的解决方法

    昨天群里有人提出了这样的问题: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 < ...

  6. 构建日均千万PV Web站点1

    如何构建日均千万PV Web站点 (一) 其实大多数互联网网站起初的网站架构都是(Linux+Apache+MySQL+PHP). 不过随着时代的发展,科技的进步.互联网进入寻常百姓家的生活.所谓的用 ...

  7. Varnish 4.0

    Varnish 4.0 实战   简介 Varnish 是一款高性能且开源的反向代理服务器和 HTTP 加速器,其采用全新的软件体系机构,和现在的硬件体系紧密配合,与传统的 squid 相比,varn ...

  8. vs2013

    vs2013使用初体验   刚安装好vs2013 , 初步体验了一把, 下面是我目前感受到的改变(对比vs2012) 1. 任务栏图标变了 (左边vs2013, 右边vs2012)    2. 开始界 ...

  9. 【hanoi】hanoi移动次数和移动步骤

    #include<stdio.h> #include<stdlib.h> //移动次数 int hanoi(int num) { ; ) { ; } else { all = ...

  10. .NET并行计算基本介绍、并行循环使用模式

    .NET并行计算基本介绍.并行循环使用模式) 阅读目录: 1.开篇介绍 2.NET并行计算基本介绍 3.并行循环使用模式 3.1并行For循环 3.2并行ForEach循环 3.3并行LINQ(PLI ...