spring mvc下shiro的session,request等问题
最近的一个项目使用的是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等问题的更多相关文章
- 理解Spring MVC Model Attribute和Session Attribute
作为一名 Java Web 应用开发者,你已经快速学习了 request(HttpServletRequest)和 session(HttpSession)作用域.在设计和构建 Java Web 应用 ...
- 【译】理解Spring MVC Model Attribute 和 Session Attribute
作为一名 Java Web 应用开发者,你已经快速学习了 request(HttpServletRequest)和 session(HttpSession)作用域.在设计和构建 Java Web 应用 ...
- 基于Spring + Spring MVC + Mybatis + shiro 高性能web构建
一直想写这篇文章,前段时间 痴迷于JavaScript.NodeJs.AngularJS,做了大量的研究,对前后端交互有了更深层次的认识. 今天抽个时间写这篇文章,我有预感,这将是一篇很详细的文章,详 ...
- spring mvc DispatcherServlet详解之一--request通过HandlerMaping获取控制器Controller过程
整个spring mvc的架构如下图所示: 现在来讲解DispatcherServletDispatcherServlet的第一步:获取控制器. HandlerMapping HandlerMappi ...
- 基于Vue+Spring MVC+MyBatis+Shiro+Dubbo开发的分布式后台管理系统
本文项目代码: 服务端:https://github.com/lining90567/dubbo-demo-server 前端:https://github.com/lining90567/dubbo ...
- Spring MVC下拉选项(Select)
以下示例显示如何在使用Spring Web MVC框架的表单中使用下拉选项(Dropdown).首先使用Eclipse IDE来创建一个WEB工程,实现一个让用户可选择自己所在的国家的功能.并按照以下 ...
- spring mvc下实现通过邮箱找回密码功能
1功能分析 通过spring mvc框架实现通过邮箱找回密码. 2 实现分析 主要是借助某个邮箱的pop3/smtp服务实现的邮件代发功能. 3 源码分析 3.1首先在用户表对应的javabean中加 ...
- spring mvc DispatcherServlet详解之三---request通过ModelAndView中获取View实例的过程
整个spring mvc的架构如下图所示: 上篇文件讲解了DispatcherServlet第二步:通过request从Controller获取ModelAndView.现在来讲解第三步:reques ...
- spring mvc mybatis shiro构建cms系统ios android
开发语言: java.ios.android 部署平台: linux.window jdk版本:JDK1.7以上版本 开发工具: eclipse.idea等 服务器中间件:Tomcat 6.7.Jbo ...
随机推荐
- MonkenRunner通过HierarchyViewer定位控件的方法和建议(Appium/UIAutomator/Robotium姊妹篇)
1. 背景 在使用MonkeyRunner的时候我们经常会用到Chimchat下面的HierarchyViewer模块来获取目标控件的一些信息来辅助我们测试,但在MonkeyRunner的官网上是没有 ...
- apache启动报错:the requested operation has failed解决办法
原因一:80端口占用 例如IIS,另外就是迅雷.我的apache服务器就是被迅雷害得无法启用! 原因二:软件冲突 装了某些软件会使apache无法启动如Dr.com 你打开网络连接->TcpIp ...
- zoj 3665 数论 二分法 两个参数
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4888 标题两个参数,途径:小参数的枚举,然后二分法大参数 想想两个点.以后就不 ...
- 增加 Java 有几个好习惯表现
以下是一些参考网络资源中的摘要Java编程在一些地方尽可能做. 1. 尝试使用单个例如在合适的场合 使用单例可以减轻负荷的负担,缩短加载时间.提高装载效率,但并不是所有的地方都适合一个案例.简单的说, ...
- HTTP 报文中的 Header 字段进行身份验证
[小技巧][ASP.Net MVC Hack] 使用 HTTP 报文中的 Header 字段进行身份验证 在一些 Web 系统中,身份验证是依靠硬件证书进行的:在电脑上插入 USB 证书,浏览器插件读 ...
- 【转】浏览器DNS 预取读技术的危害
今天中午在http://news.ycombinator.com/news看到一篇文章标题: Saved 10 billion DNS queries per month by disabling D ...
- Android开源框架Volley(Google IO 2013)源代码及内部实现分析
1.Volley概述 在项目开发过 程中,博主曾写过大量的访问网络重复代码,特别是ListView adapter很难避免getView()方法不被重复调用,如果ImageView不利用缓存机制,那么 ...
- linux 启动oracle报cannot restore segment prot after reloc: Permission denied
error while loading shared libraries: $ORACLE_HOME/lib/libnnz10.so: cannot restore segment prot afte ...
- 简话ASP.NET Web API
简话ASP.NET Web API 在vs2012中,我们很容易在根据选择的ASP.NET MVC Web应用程序来新建一个Web API应用,聪明的你一定想见得到,Web API和MVC有着某种联系 ...
- WebService和AngularJS实现模糊过滤查询
WebService和AngularJS实现模糊过滤查询 [概要] 网上看到一个不错的帖子,用WebService获取json,然后在前端使用AngularJs进行过滤搜索,看完文章后,按自己的想 ...