会话(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}.
<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 时将不能再应用