cookie和session

一、COOKIE和SESSION 区别

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

1.1 Cookie机制

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

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

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

        什么是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所示。

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

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

      不同的浏览器采用不同的方式保存Cookie。

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

      记录用户访问次数

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

      Cookie对象使用key-value键值对的形式保存用户状态,一个Cookie对象保存一个键值对,一个request或者response同时使用多个Cookie。

      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做类似的处理。

属  性  名

描    述

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规范

       Cookie的有效期

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

       如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。下面代码中的Cookie信息将永远有效。

Cookie cookie = new Cookie("name", "zhangsan");
cookie.setMaxAge(Integer.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("name", "zhangsan");
cookie.setMaxAge(0);
response.addCookie(cookie);

         注意:

         1、response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。

         要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie,达到修改的目的。删除时只需要把maxAge修改为0即可。

         2、从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。

        Cookie的修改、删除

        Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。

        如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。注意是0而不是负数。负数代表其他的意义。读者可以通过上例的程序进行验证,设置不同的属性。

       注意:修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。

        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("name", "zhangsan");// 新建Cookie
cookie.setDomain(".helloweenvsfei.com");// 设置域名
cookie.setPath("/");// 设置路径
cookie.setMaxAge(Integer.MAX_VALUE);// 设置有效期
response.addCookie(cookie);// 输出到客户端

        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。使用时一定要注意。

        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内容加密、解密,以防泄密。

        案例:永久登录

        如果用户是在自己家的电脑上上网,登录时就可以记住他的登录信息,下次访问时不需要再次登录,直接访问即可。实现方法是把登录信息如账号、密码等保存在Cookie中,并控制Cookie的有效期,下次访问时再验证Cookie中的登录信息即可。

        保存登录信息有多种方案。最直接的是把用户名与密码都保持到Cookie中,下次访问时检查Cookie中的用户名与密码,与数据库比较。这是一种比较危险的选择,一般不把密码等重要信息保存到Cookie中

        还有一种方案是把密码加密后保存到Cookie中,下次访问时解密并与数据库比较。这种方案略微安全一些。如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。

        本例将采用另一种方案,只在登录时查询一次数据库,以后访问验证登录信息时不再查询数据库。实现方式是把账号按照一定的规则加密后,连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否正确即可。本例把账号保存到名为username的Cookie中,把账号连同密钥用MD1算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的账号与密钥加密后是否与Cookie中的ssid相等。相关代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ page import="java.security.MessageDigest,java.net.URLEncoder,java.net.URLDecoder"%>
<%!
	private static final String KEY = ":cookie@helloweenvsfei.com";
 
	public final static String calcMD1(String str) { // MD1 加密算法
		str = str == null ? "" : str; // 若为null返回空
		char hexDigits[] = { '0', '1', '2', '3', '4', '1', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; // 字典
		try {
			MessageDigest mdTemp = MessageDigest.getInstance("MD5"); // 获取MD5
			mdTemp.update(str.getBytes()); // 更新数据
			byte[] md = mdTemp.digest(); // 加密
			char[] chs = new char[md.length * 2]; // 新字符数组
			int k = 0; // 计数器k
			for (int i = 0; i < md.length; i++) { // 循环输出
				chs[k++] = hexDigits[md[i] >>> 4 & 0xf];
				chs[k++] = hexDigits[md[i] & 0xf];
			}
			return new String(chs); // 加密后字符串
		} catch (Exception e) {
			return null;
		}
	}
	%>
	<%
	request.setCharacterEncoding("UTF-8");
	response.setContentType("text/html;charset=UTF-8");
 
	String action = request.getParameter("action"); // 获取action参数
	if ("login".equals(action)) { // 如果为login动作
		String username = request.getParameter("username");// 获取account参数
		String password = request.getParameter("password");// 获取password参数
		//查询数据库,验证账号密码是否正确,如果正确就登录成功
		boolean loginSuccess = true;//登录成功
		if (loginSuccess) {
			int timeout = Integer.parseInt(request.getParameter("timeout"));// 获取timeout参数
			String ssid = calcMD1(username + KEY); // 把账号、密钥使用MD1加密后保存
			Cookie usernameCookie = new Cookie("username", URLEncoder.encode(username, "UTF-8"));// 新建Cookie
			usernameCookie.setMaxAge(timeout); // 设置有效期
			Cookie ssidCookie = new Cookie("ssid", URLEncoder.encode(ssid, "UTF-8")); // 新建Cookie
			ssidCookie.setMaxAge(timeout); // 设置有效期
			response.addCookie(usernameCookie); // 输出到客户端
			response.addCookie(ssidCookie); // 输出到客户端
			// 重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容
			response.sendRedirect(request.getRequestURI() + "?" + System.currentTimeMillis());
		}
	} else if ("logout".equals(action)) { // 如果为logout动作
		Cookie usernameCookie = new Cookie("username", URLEncoder.encode("", "UTF-8"));
		// 新建Cookie,内容为空
		usernameCookie.setMaxAge(0); // 设置有效期为0,删除
		Cookie ssidCookie = new Cookie("ssid", ""); // 新建Cookie,内容为空
		ssidCookie.setMaxAge(0); // 设置有效期为0,删除
		response.addCookie(usernameCookie); // 输出到客户端
		response.addCookie(ssidCookie); // 输出到客户端
		// 重新请求本页面,参数中带有时间戳,禁止浏览器缓存页面内容
		response.sendRedirect(request.getRequestURI() + "?" + System.currentTimeMillis());
	} else {
		boolean login = false; // 是否登录
		String username = null; // 账号
		String ssid = null; // SSID标识
		Cookie[] cookies = request.getCookies();
		if (request.getCookies() != null) { // 如果Cookie不为空
			for (Cookie cookie : cookies) { // 遍历Cookie
				if (cookie.getName().equals("username")) // 如果Cookie名为account
					username = URLDecoder.decode(cookie.getValue(), "UTF-8"); // 保存account内容
				if (cookie.getName().equals("ssid")) // 如果为SSID
					ssid = URLDecoder.decode(cookie.getValue(), "UTF-8"); // 保存SSID内容
			}
		}
		if (username != null && ssid != null) { // 如果account、SSID都不为空
			login = ssid.equals(calcMD1(username + KEY));
			// 如果加密规则正确, 则视为已经登录
		}
	%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h1><%=login ? "欢迎您回来" : "请先登录"%></h1>
	<%
		if (login) {
	%>
	欢迎您, ${cookie.username.value}. &nbsp;&nbsp;
	<a href="${ pageContext.request.requestURI }?action=logout"> 注销</a>
	<%
		} else {
	%>
	<form action ="${ pageContext.request.requestURI }?action=login" method="post">
	<table>
		<tr>
			<td>账号:</td>
			<td><input type="text" name="username" style="width: 200px;"></td>
		</tr>
		<tr>
			<td>密码:</td>
			<td><input type ="password" name="password"></td>
		</tr>
		<tr>
			<td>有效期:</td>
			<td><input type ="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>
	<%
		}
	}
	%>
	
</body>
</html>

1.2 Session机制

         除了使用Cookie,Web应用程序中还经常使用Session来记录客户端状态。Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些,相应的也增加了服务器的存储压力

         什么是Session

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

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

         实现用户登录

         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属性

         注:

        1、getSession(boolean flag)详解:

        HttpSession是一个接口。
        a.当flag为true时,先查看请求当中有没有sessionId,如果没有,就会创建一个session对象。如果有sessionId,就会依据sessionId去查找对应的session对象,如果找到了就返回该对象,找不到则会创建一个新的session对象。
        b.当flag为false时,先查看请求当中有没有sessionId,如果没有,返回null。如果有sessionId,就会依据sessionId去查找对应的session对象,如果找到了就返回该对象,找不到,返回null。

        Session的生命周期

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

         Session在用户第一次访问服务器的时候自动

创建。需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session,也可以使用request.getSession(true)强制生成Session。

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

        Session的有效期

        由于会有越来越多的用户访问服务器,因此Session也会越来越多。为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。

        Session的超时时间为maxInactiveInterval属性,可以通过对应的getMaxInactiveInterval()获取,通过setMaxInactiveInterval(longinterval)修改。

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

        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的默认超时时间为30分钟。通过setMaxInactiveInterval(int seconds)修改超时时间。可以修改/WEB-INF/web.xml改变Session的默认超时时间。例如修改为60分钟:

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

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

         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地址重写。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值