码迷,mamicode.com
首页 > Web开发 > 详细

Webx学习笔记(八)Request Context之Session

时间:2015-07-08 22:10:07      阅读:135      评论:0      收藏:0      [点我收藏+]

标签:

1 Session概述

      HTTP协议是无状态的,但通过session机制,就能把无状态的变成有状态的。Session的功能就是保存HTTP请求之间的状态数据。有了session的支持,就很容易实现诸如用户登录、购物车等网站功能。在Servlet API中,有一个HttpSession的接口。

     在Java代码中访问session

//在一个请求中,保存session的状态

// 取得session对象
HttpSession session = request.getSession();

// 在session中保存用户状态
session.setAttribute("loginId", "myName");



//在另一个请求中,取出session的状态:

// 得到"myName"
String myName = (String) session.getAttribute("loginId");  

Session数据存储

1)保存在应用服务器的内存中

     一般的做法,是将session对象保存在内存里。同一时间,会有很多session被保存在服务器的内存里。由于内存是有限的,较好的服务器会把session对象的数据交换到文件中,以确保内存中的session数目保持在一个合理的范围内。

      为了提高系统扩展性和可用性,我们会使用集群技术 —— 就是一组独立的机器共同运行同一个应用。对用户来讲,集群相当于一台“大型服务器”。而实际上,同一用户的两次请求可能被分配到两台不同的服务器上来处理。这样一来,怎样保证两次请求中存取的session值一致呢?

      一种方法是使用session复制:当session的值被改变时,将它复制到其它机器上。以广播的方式随时保持着服务器同步,网络负担重,不具备高度可扩展性。

      另一种方法是TCP-Ring的方式,也就是把集群中所有的服务器看成一个环,A→B→C→D→A,首尾相接。缺点:一是配置复杂;二是每增添/减少一台机器时,ring都需要重新调整,这将成为性能瓶颈;三是要求前端的Load Balancer具有相当强的智能,才能将用户请求分发到正确的机器上。

2)保存在单一数据源中

      将session保存在单一的数据源中,这个数据源可被集群中所有的机器所共享。这样一来,就不存在复制的问题了。问题一:然而单一数据源的性能成了问题。每个用户请求,都需要访问后端的数据源(很可能是数据库)来存取用户的数据。二: 很少有应用服务器直接支持这种方案。更不用说数据源有很多种(MySQL、Oracle、Hsqldb等各种数据库、专用的session server等)了。三:数据源成了系统的瓶颈,一但这个数据源崩溃,所有的应用都不可能正常运行了。

3)保存在客户端

      把session保存在客户端。这样一来,由于不需要在服务器上保存数据,每台服务器就变得独立,能够做到线性可扩展和极高的可用性。

     具体怎么做呢?目前可用的方法,恐怕就是保存在cookie中了。但需要提醒的是,cookie具有有以下限制,因此不可无节制使用该方案:

  • Cookie数量和长度的限制。每个domain最多只能有20条cookie,每个cookie长度不能超过4KB,否则会被截掉。

  • 安全性问题。如果cookie被人拦截了,那人就可以取得所有的session信息。即使加密也与事无补,因为拦截者并不需要知道cookie的意义,他只要原样转发cookie就可以达到目的了。

  • 有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。

     虽然有上述缺点,但是对于其优点(极高的扩展性和可用性)来说,就显得微不足道。可以用下面的方法来回避上述的缺点:

  • 通过良好的编程,控制保存在cookie中的session对象的大小。

  • 通过加密和安全传输技术(SSL),减少cookie被破解的可能性。

  • 只在cookie中存放不敏感数据,即使被盗也不会有重大损失。

  • 控制cookie的生命期,使之不会永远有效。偷盗者很可能拿到一个过期的cookie。

4)将客户端、服务器端组合的方案

     任何一种session方案都有其优缺点。最好的方法是把它们结合起来。这样就可以弥补各自的缺点。

     将大部分session数据保存在cookie中,将小部分关键和涉及安全的数据保存在服务器上。由于我们只把少量关键的信息保存在服务端,因而服务器的压力不会非常大。在服务器上,单一的数据源比复制session的方案,更简单可靠。我们可以使用数据库来保存这部分session,也可以使用更廉价、更简单的存储,例如Berkeley DB就是一种不错的服务器存储方案。将session数据保存在cookie和Berkeley DB(或其它类似存储技术)中,就可以解决我们的绝大部分问题。

通用的session框架

     多数应用服务器并没有留出足够的余地,来让你自定义session的存储方案。纵使某个应用服务器提供了对外扩展的接口,可以自定义session的方案,但是我们希望保留选择应用服务器软件的自由。因此,最好的方案,不是在应用服务器上增加什么新功能,而是在WEB应用框架中实现灵活的session框架,那么我们的应用可以跑在任何标准的JavaEE应用服务器上。

     除此之外,一个好的session框架还应该做到对应用程序透明。具体表现在:

  • 使用标准的HttpSession接口,而不是增加新的API。这样任何WEB应用,都可以轻易在两种不同的session机制之间切换。

  • 应用程序不需要知道session中的对象是被保存到了cookie中还是别的什么地方。

  • Session框架可以把同一个session中的不同的对象分别保存到不同的地方去,应用程序同样不需要关心这些。例如,把一般信息放到cookie中,关键信息放到Berkeley DB中。甚至同是cookie,也有持久和临时之分,有生命期长短之分。

    Webx实现了这种session框架,把它建立在Request Contexts的基础上。

2. Session框架

1)最简配置

    例 Session框架基本配置(/WEB-INF/webx.xml

<services:request-contexts xmlns="http://www.alibaba.com/schema/services/request-contexts">
    <buffered />
    <lazy-commit />
    ...
    <session>
        <stores>
            <session-stores:simple-memory-store id="simple" /> 
        </stores>
        <store-mappings>
            <match name="*" store="simple" /> 
        </store-mappings>
    </session>
</services:request-contexts>

  以上的配置,创建了一个最基本的session实现:将所有数据(name=*)保存在内存里(simple-memory-store)。

2)Session ID

     Session ID唯一标识了一个session对象。把session ID保存在cookie里是最方便的。这样,凡是cookie值相同的所有的请求,就被看作是在同一个session中的请求。在servlet中,还可以把session ID编码到URL中。Session框架既支持把session ID保存在cookie中,也支持把session ID编码到URL中。

     完整的session ID配置如下:

<session>
    <id cookieEnabled="true" urlEncodeEnabled="false">
        <cookie name="JSESSIONID" domain="" maxAge="0" path="/" httpOnly="true" secure="false" />
        <url-encode name="JSESSIONID" />
        <session-idgens:uuid-generator />
    </id>
</session>

  上面这段配置包含了关于Session ID的所有配置以及默认值。如果不指定上述参数,则系统将使用默认值,其效果等同于上述配置。

配置<session><id> —— 将Session ID保存于何处?
cookieEnabled

是否把session ID保存在cookie中,如若不是,则只能保存的URL中。

默认为开启:true

urlEncodeEnabled

是否支持把session ID编码在URL中。如果为true开启,应用必须调用response.encodeURL()response.encodeRedirectURL()来将JSESSIONID编码到URL中。

默认为关闭:false

配置<session><id><cookie> —— 将Session ID存放于cookie的设置
name

Session ID cookie的名称。

默认为JSESSIONID

domain

Session ID cookie的domain。

默认为空,表示根据当前请求自动设置domain。这意味着浏览器认为你的cookie属于当前域名。如果你的应用包含多个子域名,例如:www.alibaba.comchina.alibaba.com,而你又希望它们能共享session的话,请把域名设置成“alibaba.com”。

maxAge

Session ID cookie的最长存活时间(秒)。

默认为0,表示临时cookie,随浏览器的关闭而消失。

path

Session ID cookie的path。

默认为/,表示根路径。

httpOnly

在session ID cookie上设置HttpOnly标记。

在IE6及更新版本中,可以缓解XSS攻击的危险。默认为true

secure

在session ID cookie上设置Secure标记。

这样,只有在https请求中才可访问该cookie。默认为false

配置<session><id><url-encode> —— 将Session ID编码到URL的设置
name

指定在URL中表示session ID的名字,默认也是JSESSIONID

此时,如果urlEncodeEnabledtrue的话,调用:

response.encodeURL("http://localhost:8080/test.jsp?id=1")

将得到类似这样的结果:

 

http://localhost:8080/test.jsp;JSESSIONID=xxxyyyzzz?id=1

 

配置<session><id><session-idgens:*> —— 如何生成session ID?
uuid-generator

以UUID作为新session ID的生成算法。

这是默认的session ID生成算法。

      为了达到最大的兼容性,我们分两种情况来处理JSESSIONID

  1. 当一个新session到达时,假如cookie或URL中已然包含了JSESSIONID,那么我们将直接利用这个值。为什么这样做呢?因为这个JSESSIONID可能是由同一域名下的另一个不相关应用生成的。如果我们不由分说地将这个cookie覆盖掉,那么另一个应用的session就会丢失。

  2. 多数情况下,对于一个新session,应该是不包含JSESSIONID的。这时,我们需要利用SessionIDGenerator来生成一个唯一的字符串,作为JSESSIONID的值。SessionIDGenerator的默认实现UUIDGenerator

3)Session的生命期

   session从创建到失效的整个过程。其状态变迁如下图所示:

技术分享

                                               图 8.1. Session生命期

   总结一下:

  1. 第一次打开浏览器时,JSESSIONID还不存在,或者存在由同一域名下的其它应用所设置的无效的JSESSIONID。这种情况下,session.isNew()返回true

  2. 随后,只要在规定的时间间隔内,以及cookie过期之前,每一次访问系统,都会使session得到更新。此时session.isNew()总是返回false。Session中的数据得到保持。

  3. 如果用户有一段时间不访问系统了,超过指定的时间,那么系统会清除所有的session内容,并将session看作是新的session。

  4. 用户可以调用session.invalidate()方法,直接清除所有的session内容。此后所有试图session.getAttribute()session.setAttribute()等操作,都会失败,得到IllegalStateException异常,直到下一个请求到来。

      在session框架中,有一个重要的特殊对象,用来保存session生命期的状态。这个对象叫作session model。它被当作一个普通的对象存放在session中,但是通过HttpSession接口不能直接看到它。

      关于session生命期的完整配置如下:

<session maxInactiveInterval="0" keepInTouch="false" forceExpirationPeriod="14400"
         modelKey="SESSION_MODEL">
    ...
</session>

  

参数名说明
maxInactiveInterval

指定session不活动而失效的期限,单位是秒。

默认为0,也就是永不失效(除非cookie失效)。例如,设置3600秒,表示用户离开浏览器1小时以后再回来,session将重新开始,老数据将被丢弃。

keepInTouch

是否每次都touch session(即更新最近访问时间)。

如果是false,那么只在session值有改变时touch。当将session model保存在cookie中时,设为false可以减少网络流量。但如果session值长期不改变,由于最近访问时间一直无法更新,将会使session超过maxInactiveInterval所设定的秒数而失效。

默认为false

forceExpirationPeriod

指定session强制作废期限,单位是秒。

无论用户活动与否,从session创建之时算起,超过这个期限,session将被强制作废。这是一个安全选项:万一cookie被盗,过了这个期限的话,那么无论如何,被盗的cookie就没有用了。

默认为0,表示无期限。

modelKey

指定用于保存session状态的对象的名称。

默认为"SESSION_MODEL"。一般没必要修改这个值。

4)Session Store

       Session Store是session框架中最核心的部分。Session框架最强大的部分就在于此。我们可以定义很多个session stores,让不同的session对象分别存放到不同的Session Store中。前面提到有一个特殊的对象“SESSION_MODEL”也必须保存在一个session store中。

技术分享

                      图 8.2. Session和Stores

      类似于Servlet的配置,Session store的配置也包含两部分内容:session store的定义,和session store的映射(mapping)。

<session>
        <stores>
            <session-stores:store id="store1" /> 
            <session-stores:store id="store2" /> 
            <session-stores:store id="store3" /> 
        </stores>
        <store-mappings>
            <match name="*" store="store1" /> 
            <match name="loginName" store="store2" /> 
            <matchRegex pattern="key.*" store="store3" /> 
        </store-mappings>
    </session>

  定义session stores:你可以配置任意多个session store,只要ID不重复。此处,store1store2store3分别是三个session store的名称。

      映射session stores:match标签用来精确匹配attribute name。一个特别的值是“*”,它代表默认匹配所有的names。本例中, 如果调用session.setAttribute("loginName", user.getId()),那么这个值将被保存到store2里;如果调用session.setAttribute("other", value)将被默认匹配到store1中。

      映射session stores:matchRegexp标签用正则表达式来匹配attribute names。本例中, key_akey_b等值都将被保存到store3里。

     注意以下几点:

  • 在整个session配置中,只能有一个store拥有默认的匹配

  • 假如有多个matchmatchRegex同时匹配某个attribute name,那么遵循以下匹配顺序:

    1. 精确的匹配最优先。

    2. 正则表达式的匹配遵循最大匹配的原则,假如有两个以上的正则表达式被同时匹配,长度较长的匹配胜出。

    3. 默认匹配*总是在所有的匹配都失败以后才会被激活。

  • 必须有一个session store能够用来存放session model。

    • 你可以用<match name="*">来匹配session model;

    • 也可以用精确匹配:<match name="SESSION_MODEL" />。其中session model的名字是必须和前述modelKey配置的值相同,其默认值为“SESSION_MODEL”。

5)Session Model

     Session Model是用来记录当前session的生命期数据的,例如:session的创建时间、最近更新时间等。默认情况下,

  • 当需要保存session数据时,SessionModel对象将被转换成一个JSON字符串(如下所示),然后这个字符串将被保存在某个session store中:

{id:"SESSION_ID",ct:创建时间,ac:最近访问时间,mx:最长不活动时间}
  • 需要读取时,先从store中读到上述格式的字符串数据,然后再把它解码成真正的SessionModel对象。

     以上转换过程是通过一个SessionModelEncoder接口来实现的。为了提供更好的移植性,Session框架可同时支持多个SessionModelEncoder的实现。配置如下:

     Session Model编码器的配置
<session>
    <session-model-encoders>
        <model-encoders:default-session-model-encoder />
        <model-encoders:model-encoder class="..." />
        <model-encoders:model-encoder class="..." />
    </session-model-encoders>
</session>

  三个SessionModelEncoder的实现。第一个是默认的实现,第二、第三个是任意实现。

  • 当从store取得SessionModel对象时,框架将依次尝试所有的encoder,直到解码成功为止。

  • 当将SessionModel对象保存到store之前,框架将使用第一个encoder来编码对象。

 

6)Session Interceptor

     Session Interceptor拦截器的作用是拦截特定的事件,甚至干预该事件的执行结果。目前有两种拦截器接口:

     Session Interceptor拦截器接口

接口功能
SessionLifecycleListener

监听以下session生命期事件:

  • Session被创建

  • Session被访问

  • Session被作废

SessionAttributeInterceptor

拦截以下session读写事件:

  • onRead – 拦截session.getAttribute()方法,可以修改所读取的数据。

  • onWrite – 拦截session.setAttribute()方法,可以修改所写到store中的数据。

     Session框架自身已经提供了两个有用的拦截器:

    Session Interceptor的实现
名称说明
<lifecycle-logger>

监听session生命期事件,并记录日志。

<attribute-whitelist>

控制session中的attributes,只允许白名单中所定义的attribute名称和类型被写入到或读出于session store中。

这个功能对于cookie store是很有用的。因为cookie有长度的限制,所以需要用白名单来限制写入到cookie中的数据数量和类型。

      可以同时配置多种拦截器,如下所示。

<session>
  <request-contexts:interceptors
        xmlns="http://www.alibaba.com/schema/services/request-contexts/session/interceptors">

    <lifecycle-logger />

    <attribute-whitelist>
      <attribute name="_csrf_token" />
      <attribute name="_lang" />
      <attribute name="loginUser" type="com.alibaba...MyUser" />
      <attribute name="shoppingCart" type="com.alibaba....ShoppingCart" />
    </attribute-whitelist>

    <interceptor class="..." />

  </request-contexts:interceptors>
</session>

  

 

Webx学习笔记(八)Request Context之Session

标签:

原文地址:http://www.cnblogs.com/Vae98Scilence/p/4631317.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!