会话(Session)跟踪是Web程序中罕用的技术,用来跟踪用户的整个会话。罕用的会话跟踪技术是Cookie与Session。Cookie通过在客户端记录信息确定用户身份Session通过在服务器端记录信息确定用户身份

本章将系统地讲述Cookie与Session机制,并比拟阐明什么时候不能用Cookie,什么时候不能用Session。

1.1  Cookie机制

在程序中,会话跟踪是很重要的事件。实践上,一个用户的所有申请操作都应该属于同一个会话,而另一个用户的所有申请操作则应该属于另一个会话,二者不能混同。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么工夫购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。

而Web应用程序是应用HTTP协定传输数据的。HTTP协定是无状态的协定。一旦数据交换结束,客户端与服务器端的连贯就会敞开,再次替换数据须要建设新的连贯。这就意味着服务器无奈从连贯上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器曾经无奈判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。

Cookie就是这样的一种机制。它能够补救HTTP协定无状态的有余。在Session呈现之前,基本上所有的网站都采纳Cookie来跟踪会话。

1.1.1  什么是Cookie

Cookie意为“甜饼”,是由W3C组织提出,最早由Netscape社区倒退的一种机制。目前Cookie曾经成为规范,所有的支流浏览器如IE、Netscape、Firefox、Opera等都反对Cookie。

因为HTTP是一种无状态的协定,服务器单从网络连接上无从晓得客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁拜访都必须携带本人通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理

Cookie实际上是一小段的文本信息。客户端申请服务器,如果服务器须要记录该用户状态,就应用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再申请该网站时,浏览器把申请的网址连同该Cookie一起提交给服务器。服务器查看该Cookie,以此来识别用户状态。服务器还能够依据须要批改Cookie的内容。

查看某个网站颁发的Cookie很简略。在浏览器地址栏输出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你的用户名Cookies”文件夹下以文本文件模式保留,一个文本文件保留一个Cookie。

1.1.2  记录用户拜访次数

Java中把Cookie封装成了javax.servlet.http.Cookie类。每个Cookie都是该Cookie类的对象。服务器通过操作Cookie类对象对客户端Cookie进行操作。通过request.getCookie()获取客户端提交的所有Cookie(以Cookie[]数组模式返回),通过response.addCookie(Cookiecookie)向客户端设置Cookie。

Cookie对象应用key-value属性对的模式保留用户状态,一个Cookie对象保留一个属性对,一个request或者response同时应用多个Cookie。因为Cookie类位于包javax.servlet.http.*上面,所以JSP中不须要import该类。

1.1.3  Cookie的不可跨域名性

很多网站都会应用Cookie。例如,Google会向客户端颁发Cookie,Baidu也会向客户端颁发Cookie。那浏览器拜访Google会不会也携带上Baidu颁发的Cookie呢?或者Google能不能批改Baidu颁发的Cookie呢?

答案是否定的。Cookie具备不可跨域名性。依据Cookie标准,浏览器拜访Google只会携带Google的Cookie,而不会携带Baidu的Cookie。Google也只能操作Google的Cookie,而不能操作Baidu的Cookie。

Cookie在客户端是由浏览器来治理的。浏览器可能保障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中能够存储二进制内容,并不实用。因为浏览器每次申请服务器都会携带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);                    // 输入到客户端

如果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即可。

留神:从客户端读取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);                       // 输入到客户端

读者能够批改本机C:WINDOWSsystem32driversetc下的hosts文件来配置多个长期域名,而后应用setCookie.jsp程序来设置跨域名Cookie验证domain属性。

留神:domain参数必须以点(".")开始。另外,name雷同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名齐全不同的网站共有Cookie,能够生成两个Cookie,domain属性别离为两个域名,输入到客户端。

1.1.10  Cookie的门路

domain属性决定运行拜访Cookie的域名,而path属性决定容许拜访Cookie的门路(ContextPath)。例如,如果只容许/sessionWeb/下的程序应用Cookie,能够这么写:

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

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

response.addCookie(cookie);                           // 输入到客户端

设置为“/”时容许所有门路应用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);                        // 输入到客户端

提醒: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  案例:永恒登录

如果用户是在本人家的电脑上上网,登录时就能够记住他的登录信息,下次访问时不须要再次登录,间接拜访即可。实现办法是把登录信息如账号、明码等保留在Cookie中,并管制Cookie的有效期,下次访问时再验证Cookie中的登录信息即可。

保留登录信息有多种计划。最间接的是把用户名与明码都放弃到Cookie中,下次访问时查看Cookie中的用户名与明码,与数据库比拟。这是一种比拟危险的抉择,个别不把明码等重要信息保留到Cookie中

还有一种计划是把明码加密后保留到Cookie中,下次访问时解密并与数据库比拟。这种计划稍微平安一些。如果不心愿保留明码,还能够把登录的工夫戳保留到Cookie与数据库中,到时只验证用户名与登录工夫戳就能够了。

这几种计划验证账号时都要查询数据库。

本例将采纳另一种计划,只在登录时查问一次数据库,当前拜访验证登录信息时不再查询数据库。实现形式是把账号依照肯定的规定加密后,连同账号一块保留到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); // 输入到客户端   response.addCookie(ssidCookie); // 输入到客户端 // 从新申请本页面,参数中带有工夫戳,禁止浏览器缓存页面内容   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); // 输入到客户端   response.addCookie(ssidCookie); // 输入到客户端 //从新申请本页面,参数中带有工夫戳,禁止浏览器缓存页面内容   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>明码: </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来记录客户端状态。Session是服务器端应用的一种记录客户端状态的机制,应用上比Cookie简略一些,相应的也减少了服务器的存储压力

1.2.1  什么是Session

Session是另一种记录客户状态的机制,不同的是Cookie保留在客户端浏览器中,而Session保留在服务器上。客户端浏览器拜访服务器的时候,服务器把客户端信息以某种模式记录在服务器上。这就是Session。客户端浏览器再次拜访时只须要从该Session中查找该客户的状态就能够了。

如果说Cookie机制是通过查看客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过查看服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建设的一份客户档案,客户来访的时候只须要查问客户档案表就能够了。

1.2.2  实现用户登录

Session对应的类为javax.servlet.http.HttpSession类。每个来访者对应一个Session对象,所有该客户的状态信息都保留在这个Session对象里。Session对象是在客户端第一次申请服务器的时候创立的。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)    { // 遍历根底数据,验证账号、明码 // 如果用户名正确且明码正确 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 = "用户名明码不匹配,登录失败。"; // 登录失败} %>

<!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不便。

当多个客户端执行程序时,服务器会保留多个客户端的Session。获取Session的时候也不须要申明获取谁的Session。Session机制决定了以后客户只会获取到本人的Session,而不会获取到他人的Session。各客户的Session也彼此独立,互不可见

提醒:Session的应用比Cookie不便,然而过多的Session存储在服务器内存中,会对服务器造成压力。

1.2.3  Session的生命周期

Session保留在服务器端。为了取得更高的存取速度,服务器个别把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于简单,当大量客户拜访服务器时可能会导致内存溢出。因而,Session里的信息应该尽量精简。

Session在用户第一次拜访服务器的时候主动创立。须要留神只有拜访JSP、Servlet等程序时才会创立Session,只拜访HTML、IMAGE等动态资源并不会创立Session。如果尚未生成Session,也能够应用request.getSession(true)强制生成Session。

Session生成后,只有用户持续拜访,服务器就会更新Session的最初拜访工夫,并保护该Session。用户每拜访服务器一次,无论是否读写Session,服务器都认为该用户的Session“沉闷(active)”了一次。

1.2.4  Session的有效期

因为会有越来越多的用户拜访服务器,因而Session也会越来越多。为避免内存溢出,服务器会把长时间内没有沉闷的Session从内存删除。这个工夫就是Session的超时工夫。如果超过了超时工夫没拜访过服务器,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由服务器主动创立,不会反复

long getCreationTime()

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

long getLastAccessedTime()

返回Session的最初沉闷工夫。返回类型为long

int getMaxInactiveInterval()

返回Session的超时工夫。单位为秒。超过该工夫没有拜访,服务器认为该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保留在服务器,对客户端是通明的,它的失常运行依然须要客户端浏览器的反对。这是因为Session须要应用Cookie作为辨认标记。HTTP协定是无状态的,Session不能根据HTTP连贯来判断是否为同一客户,因而服务器向客户端浏览器发送一个名为JSESSIONID的Cookie,它的值为该Session的id(也就是HttpSession.getId()的返回值)。Session根据该Cookie来辨认是否为同一用户。

该Cookie为服务器主动生成的,它的maxAge属性个别为–1,示意仅以后浏览器内无效,并且各浏览器窗口间不共享,敞开浏览器就会生效。

因而同一机器的两个浏览器窗口拜访服务器时,会生成两个不同的Session。然而由浏览器窗口内的链接、脚本等关上的新窗口(也就是说不是双击桌面浏览器图标等关上的窗口)除外。这类子窗口会共享父窗口的Cookie,因而会共享一个Session。

留神:新开的浏览器窗口会生成新的Session,但子窗口除外。子窗口会共用父窗口的Session。例如,在链接上右击,在弹出的快捷菜单中选择“在新窗口中关上”时,子窗口便能够拜访父窗口的Session。

如果客户端浏览器将Cookie性能禁用,或者不反对Cookie怎么办?例如,绝大多数的手机浏览器都不反对Cookie。Java Web提供了另一种解决方案:URL地址重写。

1.2.7  URL地址重写

URL地址重写是对客户端不反对Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写到URL地址中。服务器可能解析重写后的URL获取Session的id。这样即便客户端不反对Cookie,也能够应用Session来记录用户状态。HttpServletResponse类提供了encodeURL(Stringurl)实现URL地址重写,例如:

<td>

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

</td>

该办法会主动判断客户端是否反对Cookie。如果客户端反对Cookie,会将URL一成不变地输入来。如果客户端不反对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提交到服务器上,服务器通过解析URL地址取得Session的id。

如果是页面重定向(Redirection),URL地址重写能够这样写:

; "复制代码")

<%

if(“administrator”.equals(userName)){   response.sendRedirect(response.encodeRedirectURL(“administrator.jsp”));    return;} %>

; "复制代码")

成果跟response.encodeURL(String url)是一样的:如果客户端反对Cookie,生成原URL地址,如果不反对Cookie,传回重写后的带有jsessionid字符串的地址。

对于WAP程序,因为大部分的手机浏览器都不反对Cookie,WAP程序都会采纳URL地址重写来跟踪用户会话。比方用友团体的挪动商街等。

留神:TOMCAT判断客户端浏览器是否反对Cookie的根据是申请中是否含有Cookie。只管客户端可能会反对Cookie,然而因为第一次申请时不会携带任何Cookie(因为并无任何Cookie能够携带),URL地址重写后的地址中依然会带有jsessionid。当第二次拜访时服务器曾经在浏览器中写入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读写。也就是说服务器不会主动保护名为JSESSIONID的Cookie了,然而程序中依然能够读写其余的Cookie。

2 cookie和session的区别

1、cookie数据寄存在客户的浏览器上,session数据放在服务器上.

简略的说,当你登录一个网站的时候,如果web服务器端应用的是session,那么所有的数据都保留在服务器下面,

客户端每次申请服务器的时候会发送 以后会话的session_id,服务器依据以后session_id判断相应的用户数据标记,以确定用户是否登录,或具备某种权限。

因为数据是存储在服务器 下面,所以你不能伪造,然而如果你可能获取某个登录用户的session_id,用非凡的浏览器伪造该用户的申请也是可能胜利的。

session_id是服务器和客户端链接时候随机调配的,一般来说是不会有反复,但如果有大量的并发申请,也不是没有反复的可能性。

Session是由应用服务器维持的一个服务器端的存储空间,用户在连贯服务器时,会由服务器生成一个惟一的SessionID,用该SessionID 为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保留到客户端,用Cookie保留的,用户提交页面时,会将这一 SessionID提交到服务器端,来存取Session数据。这一过程,是不必开发人员干涉的。所以一旦客户端禁用Cookie,那么Session也会生效。

2、cookie不是很平安,他人能够剖析寄存在本地的COOKIE并进行COOKIE坑骗思考到平安该当应用session。

3、设置cookie工夫能够使cookie过期。然而应用session-destory(),咱们将会销毁会话。

4、session会在肯定工夫内保留在服务器上。当拜访增多,会比拟占用你服务器的性能思考到加重服务器性能方面,该当应用cookie。

5、单个cookie保留的数据不能超过4K,很多浏览器都限度一个站点最多保留20个cookie。(Session对象没有对存储的数据量的限度,其中能够保留更为简单的数据类型)

留神:

session很容易生效,用户体验很差;

尽管cookie不平安,然而能够加密 ;

cookie也分为永恒和临时存在的;

浏览器 有禁止cookie性能 ,但个别用户都不会设置;

肯定要设置生效工夫,要不然浏览器敞开就隐没了;

例如:

记住明码性能就是应用永恒cookie写在客户端电脑,下次登录时,主动将cookie信息附加发送给服务端。

application是全局性信息,是所有用户共享的信息,如能够记录有多少用户当初登录过本网站,并把该信息展现个所有用户。

两者最大的区别在于生存周期,一个是IE启动到IE敞开.(浏览器页面一关 ,session就隐没了),一个是事后设置的生存周期,或永恒的保留于本地的文件。(cookie)

Session信息是寄存在server端,但session id是寄存在client cookie的,当然php的session寄存办法是多样化的,这样就算禁用cookie一样能够跟踪

Cookie是齐全放弃在客户端的如:IE firefox 当客户端禁止cookie时将不能再应用