session串的问题以及解决方法

来源:互联网 发布:模拟人生4 网络直播 编辑:程序博客网 时间:2024/06/06 10:06

什么是session串的问题  
典型案例:现有一Web系统,用用户A登录,打开修改页面Page1,然后通过菜单文件-》新窗口打开一个窗口,用用户B登录,然后回Page1页面提交,此时身份已经变为用户B,出现非希望的结果。这就是session串的问题。

如何解决Session串的问题
解决Session串的问题需要使用URL重写的技术,下面介绍一下什么是

(1)URL重写
 什么是URL重写
  URL重写包括向URL路径添加一些容器可以解释的数据。规范要求会话ID必须编码在URL路径中,参数名称必须是jsessionid,例如: http://www.ss.com/catalog/index.html;jsessionid=1111,只有在你确定用户浏览器会把COOKIE停用的情况下才应使用URL重写,因为在这种情况下,WEB APP中的所有HYBERLINKS和FORM的ACTION URL都要使用URL重写,这样就引入一个问题就是APP中所有的页面必须是动态的页面,不能存在静态的HTML页面了,因为每个页面在往服务器发送请求的时候都要在URL上带上JSESSIONID。所有页面都用SERVLET来处理的话速度就可能变慢了。
WebLogic默认打开URL重写功能,但是这是有个前提的,要求用HttpServletResponse.encodeURL()方法来包装url,如
out.println("<a href=/"/myshop/catalog.jsp/">catalog</a>");
必需写成  
out.println("<a href=/""+response.encodeURL("myshop/catalog.jsp")+/">catalog</a>");
HttpServletResponse.encodeURL()方法检测客户端Cookie是否被禁用,如果禁用则会自动将session id加到URL中,如果没有禁用则不会添加。

具体解决方法
1.将现有系统的连接采用response.encodeURL的方式包装(工作量很大,包括href连接和js组装的连接)
2.添加一个Filter,包装HttpServletResponseWrapper,使得无论如何HttpServletResponse.encodeURL()方法都添加一个参数sid_token(值为session id)。如果是Form,则自动修改表单,添加一个隐藏字段sid_token(值为session id),同时在Filter的doFilter方法中添加一个逻辑,检查参数sid_token的值是否和当前的sesssion id一致,如果不一致则抛出异常,表示该用户已经退出系统。 

注:1.Filter是否需要自动修改表单未经考证
  2.本来想采用标准的jsessionid,但是和CAS配合时发生问题。

 

(2)ThreadLocal

ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。线程局部变量并不是Java的新发明,在其它的一些语言编译器实现(如IBM XL FORTRAN)中,它在语言的层次提供了直接的支持。因为Java中没有提供在语言层次的直接支持,而是提供了一个ThreadLocal的类来提供支持,所以,在Java中编写线程局部变量的代码相对比较笨拙,这也许是线程局部变量没有在Java中得到很好的普及的一个原因吧。

我们知道,对于每个请求容器都会开一个线程去处理,从servlet到数据库层都是同一个线程,通过ThreadLocal可以把变量在各个层共享,通过这一特性就可以解决我的问题了。
首先我实现一个filter(不知道什么是filter?),使得所有请求都通过这个filter,在filter里面获取用户选择的年份,实际上通过年份就知道了连接哪个数据库,因为系统的数据源名称是通过年份来区分的,根据年份获得数据源名称(jndi name),把jndi name存放在ThreadLocal里面。接着就要在获取数据库连接的地方获得这个jndi name。还好,程序员规范遵循得好(真的深深体会编码规范的重要性),大家统一调用同一个类来获得数据库连接,这就好办,在获取数据库连接的地方通过ThreadLocal取回jndi name,这样就获得了正确的数据库连接,代码改动量也不多。
ThreadLocal的代码如下,很简单:
public class DnsThread(){
  private static ThreadLocal tl=new ThreadLocal ();//私有静态变量  
  public static Object get(){
  return tl.get();
  }
  public static void set(Object object){
  tl.set(object);
  }  
}

原创粉丝点击