Cookie/Session机制具体解释


会话(Session)跟踪是Web程序中常用的技术,用来跟踪用户的整个会话。常用的会话跟踪技术是Cookie与Session。Cookie经过在client记录信息肯定用户身份Session经过在server端记录信息肯定用户身份javascript

本章将系统地讲述Cookie与Session机制,并比較说明何时不能用Cookie,何时不能用Session。html


1.1  Cookie机制java

在程序中,会话跟踪是很是重要的事情。理论上,一个用户的所有请求操做都应该属于同一个会话,而还有一个用户的所有请求操做则应该属于还有一个会话,两者不能混淆。好比,用户A在超市购买的不论什么商品都应该放在A的购物车内,不管是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。git

而Web应用程序是使用HTTP协议数据传输的。HTTP协议是无状态的协议。一旦数据交换完成,client与server端的链接就会关闭,再次交换数据需要创建新的链接。这就意味着server没法从链接上跟踪会话即用户A购买了一件商品放入购物车内,当再次购买商品时server已经没法推断该购买行为是属于用户A的会话仍是用户B的会话了。要跟踪该会话,必须引入一种机制。web

Cookie就是这种一种机制。它可以弥补HTTP协议无状态的不足。在Session出现以前,基本上所有的站点都採用Cookie来跟踪会话。算法

1.1.1  什么是Cookie数据库

Cookie意为“甜饼”,是由W3C组织提出,最先由Netscape社区发展的一种机制。眼下Cookie已经成为标准,所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。编程

由于HTTP是一种无状态的协议,server单从网络链接上无从知道客户身份。怎么办呢?就给client们颁发一个通行证吧,每人一个,无论谁訪问都必须携带本身通行证。这样server就能从通行证上确认客户身份了。这就是Cookie的工做原理跨域

Cookie其实是一小段的文本信息。client请求server,假设server需要记录该用户状态,就使用response向client浏览器颁发一个Cookie。client浏览器会把Cookie保存起来。当浏览器再请求该站点时,浏览器把请求的网址连同该Cookie一同提交给server。server检查该Cookie,以此来辨认用户状态。server还可以依据需要改动Cookie的内容。数组



查看某个站点颁发的Cookie很是easy。在浏览器地址栏输入javascript:alert (document. cookie)就可以了(需要有网才干查看)。JavaScript脚本会弹出一个对话框显示本站点颁发的所有Cookie的内容,如图1.1所看到的。


图1.1  Baidu站点颁发的Cookie


图1.1中弹出的对话框中显示的为Baidu站点的Cookie。当中第一行BAIDUID记录的就是笔者的身份helloweenvsfei,仅仅是Baidu使用特殊的方法将Cookie信息加密了。


注意:Cookie功能需要浏览器的支持。

假设浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。

不一样的浏览器採用不一样的方式保存Cookie。

IE浏览器会在“C:\Documents and Settings\你的username\Cookies”目录下以文本文件形式保存,一个文本文件保存一个Cookie。


1.1.2  记录用户訪问次数

Java中把Cookie封装成了javax.servlet.http.Cookie类。每个Cookie都是该Cookie类的对象。server经过操做Cookie类对象对clientCookie进行操做。经过request.getCookie()获取client提交的所有Cookie(以Cookie[]数组形式返回),经过response.addCookie(Cookiecookie)向client设置Cookie。

Cookie对象使用key-value属性对的形式保存用户状态,一个Cookie对象保存一个属性对,一个request或者response同一时候使用多个Cookie。因为Cookie类位于包javax.servlet.http.*如下,因此JSP中不需要import该类。


1.1.3  Cookie的不可跨域名性

很是多站点都会使用Cookie。好比,Google会向client颁发Cookie,Baidu也会向client颁发Cookie。那浏览器訪问Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能改动Baidu颁发的Cookie呢?

答案是否认的。Cookie具备不可跨域名性依据Cookie规范,浏览器訪问Google仅仅会携带Google的Cookie,而不会携带Baidu的Cookie。Google也仅仅能操做Google的Cookie,而不能操做Baidu的Cookie。

Cookie在client是由浏览器来管理的。浏览器能够保证Google仅仅会操做Google的Cookie而不会操做Baidu的Cookie,从而保证用户的隐私安全。浏览器推断一个站点可否操做还有一个站点Cookie的根据是域名。Google与Baidu的域名不同,所以Google不能操做Baidu的Cookie。

需要注意的是,尽管站点images.google.com与站点www.google.com同属于Google,但是域名不同,两者相同不能互相操做彼此的Cookie。


注意:用户登陆站点www.google.com以后会发现訪问images.google.com时登陆信息仍然有效,而普通的Cookie是作不到的。这是因为Google作了特殊处理。本章后面也会对Cookie作相似的处理。


1.1.4  Unicode编码:保存中文

中文与英文字符不一样,中文属于Unicode字符,在内存中占4个字符,而英文属于ASCII字符,内存中仅仅占2个字节。Cookie中使用Unicode字符时需要对Unicode字符进行编码,不然会乱码。


提示:Cookie中保存中文仅仅能编码。通常使用UTF-8编码就能够。不推荐使用GBK等中文编码,因为浏览器不必定支持,而且JavaScript也不支持GBK编码。


1.1.5  BASE64编码:保存二进制图片

Cookie不只可以使用ASCII字符与Unicode字符,还可以使用二进制数据。好比在Cookie中使用数字证书,提供安全度。使用二进制数据时也需要进行编码。

%注意:本程序仅用于展现Cookie中可以存储二进制内容,并不有用。由于浏览器每次请求server都会携带Cookie,所以Cookie内容不宜过多,不然影响速度。Cookie的内容应该少而精。


1.1.6  设置Cookie的所有属性

除了name与value以外,Cookie还具备其它几个常用的属性。每个属性相应一个getter方法与一个setter方法。Cookie类的所有属性如表1.1所看到的。

表1.1  Cookie常用属性

属  性  名

描    述

String name

该Cookie的名称。Cookie一旦建立,名称便不可更改

Object value

该Cookie的值。假设值为Unicode字符,需要为字符编码。假设值为二进制数据,则需要使用BASE64编码

int maxAge

该Cookie失效的时间,单位秒。假设为正数,则该Cookie在maxAge秒以后失效。假设为负数,该Cookie为暂时Cookie,关闭浏览器即失效,浏览器也不会以不论什么形式保存该Cookie。假设为0,表示删除该Cookie。默以为–1

boolean secure

该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS,SSL等,在网络上数据传输以前先将数据加密。默以为false

String path

该Cookie的使用路径。假设设置为“/sessionWeb/”,则仅仅有contextPath为“/sessionWeb”的程序可以訪问该Cookie。假设设置为“/”,则本域名下contextPath都可以訪问该Cookie。注意最后一个字符必须为“/”

String domain

可以訪问该Cookie的域名。假设设置为“.google.com”,则所有以“google.com”结尾的域名都可以訪问该Cookie。注意第一个字符必须为“.”

String comment

该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明

int version

该Cookie使用的版本。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范


1.1.7  Cookie的有效期

Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。Cookie中经过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。

假设maxAge属性为正数,则表示该Cookie会在maxAge秒以后本身主动失效。浏览器会将maxAge为正数的Cookie持久化,即写到相应的Cookie文件里。无论客户关闭了浏览器仍是电脑,仅仅要还在maxAge秒以前,登陆站点时该Cookie仍然有效。如下代码中的Cookie信息将永远有效。


Cookie cookie = new Cookie("username","helloweenvsfei");   // 新建Cookie

cookie.setMaxAge(Integer.MAX_VALUE);           // 设置生命周期为MAX_VALUE

response.addCookie(cookie);                    // 输出到client


假设maxAge为负数,则表示该Cookie仅在本浏览器窗体以及本窗体打开的子窗体内有效,关闭窗体后该Cookie即失效。maxAge为负数的Cookie,为暂时性Cookie,不会被持久化,不会被写到Cookie文件里。Cookie信息保存在浏览器内存中,所以关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。

假设maxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法,所以经过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除,


好比:

Cookie cookie = new Cookie("username","helloweenvsfei");   // 新建Cookie

cookie.setMaxAge(0);                          // 设置生命周期为0,不能为负数

response.addCookie(cookie);                    // 必须运行这一句


response对象提供的Cookie操做方法仅仅有一个加入操做add(Cookie cookie)。

要想改动Cookie仅仅能使用一个同名的Cookie来覆盖原来的Cookie,达到改动的目的。删除时仅仅需要把maxAge改动为0就能够。


注意:从client读取Cookie时,包含maxAge在内的其它属性都是不可读的,也不会被提交。浏览器提交Cookie时仅仅会提交name与value属性。maxAge属性仅仅被浏览器用来推断Cookie是否过时。


1.1.8  Cookie的改动、删除

Cookie并不提供改动、删除操做。假设要改动某个Cookie,仅仅需要新建一个同名的Cookie,加入到response中覆盖原来的Cookie。

假设要删除某个Cookie,仅仅需要新建一个同名的Cookie,并将maxAge设置为0,并加入到response中覆盖原来的Cookie。注意是0而不是负数。负数表明其它的意义。读者可以经过上例的程序进行验证,设置不一样的属性。


注意:改动、删除Cookie时,新建的Cookie除value、maxAge以外的所有属性,好比name、path、domain等,都要与原Cookie全然同样。不然,浏览器将视为两个不一样的Cookie不予覆盖,致使改动、删除失败。


1.1.9  Cookie的域名

Cookie是不可跨域名的。域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止站点非法获取其它站点的Cookie。

正常状况下,同一个一级域名下的两个二级域名如www.helloweenvsfei.com和images.helloweenvsfei.com也不能交互使用Cookie,因为两者的域名并不严格一样。假设想所有helloweenvsfei.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain參数,好比:

Cookie cookie = new Cookie("time","20080808"); // 新建Cookie

cookie.setDomain(".helloweenvsfei.com");           // 设置域名

cookie.setPath("/");                              // 设置路径

cookie.setMaxAge(Integer.MAX_VALUE);               // 设置有效期

response.addCookie(cookie);                       // 输出到client


读者可以改动本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个暂时域名,而后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。

注意:domain參数必须以点(".")開始。另外,name一样但domain不一样的两个Cookie是两个不一样的Cookie。假设想要两个域名全然不一样的站点共同拥有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到client。


1.1.10  Cookie的路径

domain属性决定执行訪问Cookie的域名,而path属性决定赞成訪问Cookie的路径(ContextPath)。好比,假设仅仅赞成/sessionWeb/下的程序使用Cookie,可以这么写:

Cookie cookie = new Cookie("time","20080808");     // 新建Cookie

cookie.setPath("/session/");                          // 设置路径

response.addCookie(cookie);                           // 输出到client

设置为“/”时赞成所有路径使用Cookie。path属性需要使用符号“/”结尾。name一样但domain一样的两个Cookie也是两个不一样的Cookie。


注意:页面仅仅能获取它属于的Path的Cookie。好比/session/test/a.jsp不能获取到路径为/session/abc/的Cookie。使用时必定要注意。


1.1.11  Cookie的安全属性

HTTP协议不只是无状态的,而且是不安全的。使用HTTP协议的数据不通过不论什么加密就直接在网络上传播,有被截获的可能。使用HTTP协议传输很是机密的内容是一种隐患。假设不但愿Cookie在HTTP等非安全协议中传输,可以设置Cookie的secure属性为true。浏览器仅仅会在HTTPS和SSL等安全协议中传输此类Cookie。如下的代码设置secure属性为true:


Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie

cookie.setSecure(true);                           // 设置安全属性

response.addCookie(cookie);                        // 输出到client


提示:secure属性并不能对Cookie内容加密,于是不能保证绝对的安全性。假设需要高安全性,需要在程序中对Cookie内容加密、解密,以防泄密。


1.1.12  JavaScript操做Cookie

Cookie是保存在浏览器端的,所以浏览器具备操做Cookie的先决条件。浏览器可以使用脚本程序如JavaScript或者VBScript等操做Cookie。这里以JavaScript为例介绍常用的Cookie操做。好比如下的代码会输出本页面所有的Cookie。

<script>document.write(document.cookie);</script>

由于JavaScript能够随意地读写Cookie,有些好事者便想使用JavaScript程序去窥探用户在其它站点的Cookie。只是这是徒劳的,W3C组织早就意识到JavaScript对Cookie的读写所带来的安全隐患并加以防备了,W3C标准的浏览器会阻止JavaScript读写不论什么不属于本身站点的Cookie。换句话说,A站点的JavaScript程序读写B站点的Cookie不会有不论什么结果。


1.1.13  案例:永久登陆

假设用户是在本身家的电脑上上网,登陆时就可以记住他的登陆信息,下次訪问时不需要再次登陆,直接訪问就能够。实现方法是把登陆信息如帐号、password等保存在Cookie中,并控制Cookie的有效期,下次訪问时再验证Cookie中的登陆信息就能够。

保存登陆信息有多种方案。最直接的是把username与password都保持到Cookie中,下次訪问时检查Cookie中的username与password,与数据库比較。这是一种比較危急的选择,通常不把password等重要信息保存到Cookie中

还有一种方案是把password加密后保存到Cookie中,下次訪问时解密并与数据库比較这样的方案稍微安全一些。假设不但愿保存password,还可以把登陆的时间戳保存到Cookie与数据库中,到时仅仅验证username与登陆时间戳就可以了。

这几种方案验证帐号时都要查询数据库。

本例将採用还有一种方案,仅仅在登陆时查询一次数据库,之后訪问验证登陆信息时再也不查询数据库。实现方式是把帐号依照必定的规则加密后,连同帐号一块保存到Cookie中。下次訪问时仅仅需要推断帐号的加密规则是否正确就能够本例把帐号保存到名为account的Cookie中,把帐号连同密钥用MD1算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的帐号与密钥加密后是否与Cookie中的ssid相等。相关代码例如如下:

代码1.8 loginCookie.jsp

<%@ page language="java"pageEncoding="UTF-8" isErrorPage="false" %>

<%!                                                  // JSP方法

    private static final String KEY =":cookie@helloweenvsfei.com";
                                                     // 密钥 

    public final static String calcMD1(Stringss) { // MD1 加密算法

       String s = ss==null ?"" : ss;                  // 若为null返回空

       char hexDigits[] = { '0','1', '2', '3', '4', '1', '6', '7', '8', '9',
       'a', 'b', 'c', 'd', 'e', 'f' };                        // 字典

       try {

        byte[] strTemp =s.getBytes();                          // 获取字节

        MessageDigestmdTemp = MessageDigest.getInstance("MD1"); // 获取MD1

       mdTemp.update(strTemp);                                // 更新数据

        byte[] md =mdTemp.digest();                        // 加密

        int j =md.length;                                 // 加密后的长度

        char str[] = newchar[j * 2];                       // 新字符串数组

        int k =0;                                         // 计数器k

        for (int i = 0; i< j; i++) {                       // 循环输出

         byte byte0 =md[i];

         str[k++] =hexDigits[byte0 >>> 4 & 0xf];

         str[k++] =hexDigits[byte0 & 0xf];

        }

        return newString(str);                             // 加密后字符串

       } catch (Exception e){return null; }

    }

%>

<%

   request.setCharacterEncoding("UTF-8");             // 设置request编码

    response.setCharacterEncoding("UTF-8");        // 设置response编码

   

    String action =request.getParameter("action"); // 获取action參数

   

    if("login".equals(action)){                       // 假设为login动做

        String account =request.getParameter("account");
                                                     // 获取account參数

        String password =request.getParameter("password");
                                                     // 获取password參数

        int timeout = newInteger(request.getParameter("timeout"));
                                                     // 获取timeout參数

              

        String ssid =calcMD1(account + KEY); // 把帐号、密钥使用MD1加密后保存

       

        CookieaccountCookie = new Cookie("account", account);
                                                     // 新建Cookie

       accountCookie.setMaxAge(timeout);              // 设置有效期

       

        Cookie ssidCookie =new Cookie("ssid", ssid);   // 新建Cookie

       ssidCookie.setMaxAge(timeout);                 // 设置有效期

       

       response.addCookie(accountCookie);             // 输出到client

       response.addCookie(ssidCookie);            // 输出到client

       

        // 又一次请求本页面,參数中带有时间戳,禁止浏览器缓存页面内容

       response.sendRedirect(request.getRequestURI() + "?" + System.
        currentTimeMillis());

        return;

    }

    elseif("logout".equals(action)){                  // 假设为logout动做

       

        CookieaccountCookie = new Cookie("account", "");
                                                 // 新建Cookie,内容为空

       accountCookie.setMaxAge(0);                // 设置有效期为0,删除

              

        Cookie ssidCookie =new Cookie("ssid", ""); // 新建Cookie,内容为空

       ssidCookie.setMaxAge(0);                   // 设置有效期为0,删除

       response.addCookie(accountCookie);         // 输出到client

       response.addCookie(ssidCookie);         // 输出到client

        //又一次请求本页面,參数中带有时间戳,禁止浏览器缓存页面内容

       response.sendRedirect(request.getRequestURI() + "?" + System.
        currentTimeMillis());

        return;

    }

    boolean login = false;                        // 是否登陆

    String account = null;                        // 帐号

    String ssid = null;                           // SSID标识

   

    if(request.getCookies() !=null){               // 假设Cookie不为空

        for(Cookie cookie :request.getCookies()){  // 遍历Cookie

           if(cookie.getName().equals("account"))  // 假设Cookie名为
                                                    account

               account = cookie.getValue();       // 保存account内容

           if(cookie.getName().equals("ssid")) // 假设为SSID

               ssid = cookie.getValue();          // 保存SSID内容

        }

    }

    if(account != null && ssid !=null){    // 假设account、SSID都不为空

        login =ssid.equals(calcMD1(account + KEY));
                                      // 假设加密规则正确, 则视为已经登陆

    }

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">

       <legend><%= login ? "欢迎您回来" : "请先登陆"%></legend>

        <% if(login){%>

            欢迎您, ${ cookie.account.value }. &nbsp;&nbsp;

           <a href="${ pageContext.request.requestURI }?action=logout">
            注销</a>

        <% } else {%>

        <formaction="${ pageContext.request.requestURI }?action=login"
        method="post">

           <table>

               <tr><td>帐号: </td>

                   <td><input type="text"name="account" style="width:
                   200px; "></td>

               </tr>

               <tr><td>password: </td>

                   <td><inputtype="password" name="password"></td>

               </tr>

               <tr>

                   <td>有效期: </td>

                   <td><inputtype="radio" name="timeout" value="-1"
                   checked> 关闭浏览器即失效 <br/> <input type="radio"
                   name="timeout" value="<%= 30 *24 * 60 * 60 %>"> 30天
                   内有效 <br/><input type="radio" name="timeout" value=
                   "<%= Integer.MAX_VALUE %>"> 永久有效 <br/> </td> </tr>

               <tr><td></td>

                   <td><input type="submit"value=" 登  录 " class=
                   "button"></td>

               </tr>

           </table>

        </form>

        <% } %>

登陆时可以选择登陆信息的有效期:关闭浏览器即失效、30天内有效与永久有效。经过设置Cookie的age属性来实现,注意观察代码。执行效果如图1.7所看到的。


图1.7  永久登陆

提示:该加密机制中最重要的部分为算法与密钥。由于MD1算法的不可逆性,即便用户知道了帐号与加密后的字符串,也不可能解密获得密钥。所以,仅仅要保管好密钥与算法,该机制就是安全的。


1.2  Session机制

除了使用Cookie,Web应用程序中还经常使用Session来记录client状态。Session是server端使用的一种记录client状态的机制,使用上比Cookie简单一些,对应的也添加了server的存储压力

1.2.1  什么是Session

Session是还有一种记录客户状态的机制,不一样的是Cookie保存在client浏览器中,而Session保存在server上。client浏览器訪问server的时候,server把client信息以某种形式记录在server上。这就是Session。client浏览器再次訪问时仅仅需要从该Session中查找该客户的状态就可以了。

假设说Cookie机制是经过检查客户身上的“通行证”来肯定客户身份的话,那么Session机制就是经过检查server上的“客户明细表”来确认客户身份。Session至关于程序在server上创建的一份客户档案,客户来訪的时候仅仅需要查询客户档案表就可以了。

1.2.2  实现用户登陆

Session相应的类为javax.servlet.http.HttpSession类。每个来訪者相应一个Session对象,所有该客户的状态信息都保存在这个Session对象里。Session对象是在client第一次请求server的时候建立的。Session也是一种key-value的属性对,经过getAttribute(Stringkey)和setAttribute(String key,Objectvalue)方法读写客户状态信息。Servlet里经过request.getSession()方法获取该客户的Session,

好比:

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

session.setAttribute("loginTime", new Date());     // 设置Session中的属性

   

out.println("登陆时间为:" +(Date)session.getAttribute("loginTime"));      // 获取Session属性

request还可以使用getSession(boolean create)来获取Session。差异是假设该客户的Session不存在,request.getSession()方法会返回null,而getSession(true)会先建立Session再将Session返回。

Servlet中必须使用request来编程式获取HttpSession对象,而JSP中内置了Session隐藏对象,可以直接使用。假设使用声明了<%@page session="false" %>,则Session隐藏对象不可用。如下的样例使用Session记录客户帐号信息。

源码例如如下:

代码1.9  session.jsp

<%@ page language="java" pageEncoding="UTF-8"%>

<jsp:directive.page import="com.helloweenvsfei.sessionWeb.bean.Person"/>

<jsp:directive.page import="java.text.SimpleDateFormat"/>

<jsp:directive.page import="java.text.DateFormat"/>

<jsp:directive.page import="java.util.Date"/>

<%!

    DateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");         // 日期格式化器

%>

<%

    response.setCharacterEncoding("UTF-8");        // 设置request编码

    Person[] persons =

    {          

       // 基础数据,保存三我的的信息

        new Person("Liu Jinghua","password1", 34, dateFormat.parse
        ("1982-01-01")),

        new Person("Hello Kitty","hellokitty", 23, dateFormat.parse
        ("1984-02-21")),

        new Person("Garfield", "garfield_pass",23, dateFormat.parse
        ("1994-09-12")),

     };

   

    String message = "";                      // 要显示的消息

   

    if(request.getMethod().equals("POST"))

    {

        // 假设是POST登陆       

        for(Person person :persons)

        {          

            // 遍历基础数据,验证帐号、password

            // 假设username正确且password正确

           if(person.getName().equalsIgnoreCase(request.getParameter("username"))&&person.getPassword().equals(request.getParameter("password")))

           {              

               // 登陆成功,设置将用户的信息以及登陆时间保存到Session

               session.setAttribute("person", person);                   // 保存登陆的Person

               session.setAttribute("loginTime", new Date());          // 保存登陆的时间              

               response.sendRedirect(request.getContextPath() + "/welcome.jsp");

               return;

            }

        }      

        message = "usernamepassword不匹配,登陆失败。";       // 登陆失败

    }

%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN">

<html>

    // ... HTML代码为一个FORM表单,代码略,请看随书光盘

</html>


登陆界面验证用户登陆信息,假设登陆正确,就把用户信息以及登陆时间保存进Session,而后转到欢迎页面welcome.jsp。welcome.jsp中从Session中获取信息,并将用户资料显示出来。

welcome.jsp代码例如如下:

代码1.10  welcome.jsp

<%@ page language="java" pageEncoding="UTF-8"%>

<jsp:directive.pageimport="com.helloweenvsfei.sessionWeb.bean.Person"/>

<jsp:directive.page import="java.text.SimpleDateFormat"/>

<jsp:directive.page import="java.text.DateFormat"/>

<jsp:directive.page import="java.util.Date"/>

<%!

    DateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");         // 日期格式化器

%>

<%

    Person person =(Person)session.getAttribute("person");                       // 获取登陆的person

    Date loginTime =(Date)session.getAttribute("loginTime");                     // 获取登陆时间

%>

    // ... 部分HTML代码略

            <table>

               <tr><td>您的姓名:</td>

                   <td><%= person.getName()%></td>

               </tr>

               <tr><td>登陆时间:</td>

                   <td><%= loginTime%></td>

               </tr>

               <tr><td>您的年龄:</td>

                   <td><%= person.getAge()%></td>

               </tr>

               <tr><td>您的生日:</td>

                   <td><%=dateFormat.format(person.getBirthday()) %></td>

               </tr>

            </table>

程序执行效果如图1.8所看到的。


图1.8  使用Session记录用户信息

注意程序中Session中直接保存了Person类对象与Date类对象,使用起来要比Cookie方便。

当多个client运行程序时,server会保存多个client的Session。获取Session的时候也不需要声明获取谁的Session。Session机制决定了当前客户仅仅会获取到本身的Session,而不会获取到别人的Session。各客户的Session也彼此独立,互不可见


提示Session的使用比Cookie方便,但是过多的Session存储在server内存中,会对server形成压力。


1.2.3  Session的生命周期

Session保存在server端。为了得到更高的存取速度,server通常把Session放在内存里。每个用户都会有一个独立的Session。假设Session内容过于复杂,当大量客户訪问server时可能会致使内存溢出。所以,Session里的信息应该尽可能精简。

Session在用户第一次訪问server的时候本身主动建立。需要注意仅仅有訪问JSP、Servlet等程序时才会建立Session,仅仅訪问HTML、IMAGE等静态资源并不会建立Session。假设还没有生成Session,也可以使用request.getSession(true)强制生成Session。

Session生成后,仅仅要用户继续訪问,server就会更新Session的最后訪问时间,并维护该Session用户每訪问server一次,无论是否读写Session,server都以为该用户的Session“活跃(active)”了一次。


1.2.4  Session的有效期

由于会有愈来愈多的用户訪问server,所以Session也会愈来愈多。为防止内存溢出,server会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。假设超过了超时时间没訪问过server,Session就本身主动失效了。

Session的超时时间为maxInactiveInterval属性,可以经过相应的getMaxInactiveInterval()获取,经过setMaxInactiveInterval(longinterval)改动。

Session的超时时间也可以在web.xml中改动。另外,经过调用Session的invalidate()方法可以使Session失效。


1.2.5  Session的常常用法

Session中包含各类方法,使用起来要比Cookie方便得多。Session的常常用法如表1.2所看到的。

表1.2  HttpSession的常常用法

方  法  名

描    述

void setAttribute(String attribute, Object value)

设置Session属性。value參数可以为不论什么Java Object。一般为Java Bean。value信息不宜过大

String getAttribute(String attribute)

返回Session属性

Enumeration getAttributeNames()

返回Session中存在的属性名

void removeAttribute(String attribute)

移除Session属性

String getId()

返回Session的ID。该ID由server本身主动建立,不会反复

long getCreationTime()

返回Session的建立日期。返回类型为long,常被转化为Date类型,好比:Date createTime = new Date(session.get CreationTime())

long getLastAccessedTime()

返回Session的最后活跃时间。返回类型为long

int getMaxInactiveInterval()

返回Session的超时时间。单位为秒。超过该时间没有訪问,server以为该Session失效

void setMaxInactiveInterval(int second)

设置Session的超时时间。单位为秒

void putValue(String attribute, Object value)

不推荐的方法。已经被setAttribute(String attribute, Object Value)替代

Object getValue(String attribute)

不被推荐的方法。已经被getAttribute(String attr)替代

boolean isNew()

返回该Session是不是新建立的

void invalidate()

使该Session失效

Tomcat中Session的默认超时时间为20分钟。经过setMaxInactiveInterval(int seconds)改动超时时间。可以改动web.xml改变Session的默认超时时间。好比改动为60分钟:

<session-config>

   <session-timeout>60</session-timeout>      <!-- 单位:分钟 -->

</session-config>


注意:<session-timeout>參数的单位为分钟,而setMaxInactiveInterval(int s)单位为秒。


1.2.6  Session对浏览器的要求

尽管Session保存在server,对client是透明的,它的正常执行仍然需要client浏览器的支持。这是因为Session需要使用Cookie做为识别标志。HTTP协议是无状态的,Session不能根据HTTP链接来推断是否为同一客户,所以server向client浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session根据该Cookie来识别是否为同一用户。

该Cookie为server本身主动生成的,它的maxAge属性通常为–1,表示仅当前浏览器内有效,并且各浏览器窗体间不共享,关闭浏览器就会失效。

所以同一机器的两个浏览器窗体訪问server时,会生成两个不一样的Session。但是由浏览器窗体内的连接、脚本等打开的新窗体(也就是说不是双击桌面浏览器图标等打开的窗体)除外。这类子窗体会共享父窗体的Cookie,所以会共享一个Session。


注意:新开的浏览器窗体会生成新的Session,但子窗体除外。子窗体会共用父窗体的Session。好比,在连接上右击,在弹出的快捷菜单中选择“在新窗体中打开”时,子窗体便可以訪问父窗体的Session。

假设client浏览器将Cookie功能禁用,或者不支持Cookie怎么办?好比,绝大多数的手机浏览器都不支持Cookie。Java Web提供了还有一种解决方式:URL地址重写。


1.2.7  URL地址重写

URL地址重写是对client不支持Cookie的解决方式。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。server能够解析重写后的URL获取Session的id。这样即便client不支持Cookie,也可使用Session来记录用户状态。HttpServletResponse类提供了encodeURL(Stringurl)实现URL地址重写,好比:

<td>

    <a href="<%=response.encodeURL("index.jsp?c=1&wd=Java") %>">
    Homepage</a>

</td>

该方法会本身主动推断client是否支持Cookie。假设client支持Cookie,会将URL原封不动地输出来。假设client不支持Cookie,则会将用户Session的id重写到URL中。重写后的输出多是这种:

<td>

    <ahref="index.jsp;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E?c=
    1&wd=Java">Homepage</a>

</td>

即在文件名称的后面,在URL參数的前面加入了字符串“;jsessionid=XXX”。当中XXX为Session的id。分析一下可以知道,增添的jsessionid字符串既不会影响请求的文件名称,也不会影响提交的地址栏參数。用户单击这个连接的时候会把Session的id经过URL提交到server上,server经过解析URL地址得到Session的id。

假设是页面重定向(Redirection),URL地址重写可以这样写:

<%

    if(“administrator”.equals(userName))

    {

       response.sendRedirect(response.encodeRedirectURL(“administrator.jsp”));

        return;

    }

%>

效果跟response.encodeURL(String url)是同样的:假设client支持Cookie,生成原URL地址,假设不支持Cookie,传回重写后的带有jsessionid字符串的地址。

对于WAP程序,由于大部分的手机浏览器都不支持Cookie,WAP程序都会採用URL地址重写来跟踪用户会话。比方用友集团的移动商街等。


注意:TOMCAT推断client浏览器是否支持Cookie的根据是请求中是否含有Cookie。虽然client可能会支持Cookie,但是因为第一次请求时不会携带不论什么Cookie(因为并没有不论什么Cookie可以携带),URL地址重写后的地址中仍然会带有jsessionid。当第二次訪问时server已经在浏览器中写入Cookie了,所以URL地址重写后的地址中就不会带有jsessionid了。


1.2.8  Session中禁止使用Cookie

既然WAP上大部分的客户浏览器都不支持Cookie,索性禁止Session使用Cookie,统一使用URL地址重写会更好一些。Java Web规范支持经过配置的方式禁用Cookie。如下举例说一下如何经过配置禁止使用Cookie。

打开项目sessionWeb的WebRoot目录下的META-INF目录(跟WEB-INF目录同级,假设没有则建立),打开context.xml(假设没有则建立),编辑内容例如如下:

代码1.11 /META-INF/context.xml

<?xml version='1.0' encoding='UTF-8'?>

<Context path="/sessionWeb"cookies="false">

</Context>


或者改动Tomcat全局的conf/context.xml,改动内容例如如下:

代码1.12  context.xml

<!-- The contents of this file will be loaded for eachweb application -->

<Context cookies="false">

    <!-- ... 中间代码略 -->

</Context>

部署后TOMCAT便不会本身主动生成名JSESSIONID的Cookie,Session也不会以Cookie为识别标志,而只以重写后的URL地址为识别标志了。


注意:该配置仅仅是禁止Session使用Cookie做为识别标志,并不能阻止其它的Cookie读写。也就是说server不会本身主动维护名为JSESSIONID的Cookie了,但是程序中仍然可以读写其它的Cookie。

相关文章
相关标签/搜索