深入理解Session和 Cookie

本文详细介绍了Web应用中Session与Cookie的工作原理及使用方法,对比两者差异,解释它们如何帮助跟踪用户会话。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先,Session与Cookie的作用都是为了保持访问用户与后端服务器的交互状态,也就是跟踪用户的整个会话。不同的是,Cookie通过在客户端记录信息确定用户身份,Session通过在服务器端记录信息确定用户身份。

一、理解Cookie

1、Cookie为什么会出现?

Web应用程序是使用HTTP协议传输数据的, HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
注意:Cookie功能需要浏览器的支持。如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。不同的浏览器采用不同的方式保存Cookie。

2、Cookie的属性项

属性项属性项介绍
NAME=VALUE键值对,可以设置要保存的Key/Value,注意这里的NAME不能和其他属性项的名字一样
Expires过期时间,在设置的某个时间点后该Cookie就会失效,如expires=Wednesday,16-Nov-17 23:12:40 GMT
Max-Age最大失效时间,表示在多少秒后失效
Domain生成该Cookie的域名,即可以访问该Cookie的域名
Path生成该Cookie的路径,即可以访问该Cookie的路径,设置为“/”,则本域名下contextPath都可以访问该Cookie,最后一个字符必须为“/”
Secure如果设置了这个属性,那么只会在使用安全协议连接时才会回传该Cookie,安全协议有HTTPS,SSL等。默认为false。
Comment注释项,说明该Cookie有何用途
Version该Cookie的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范
Port该Cookie在什么端口下可以回传服务端,如果有多个端口,用逗号隔开
Discard是否在会话结束后丢弃该Cookie项,默认为false

下面详细介绍Cookie的这些属性项:
2.1 Cookie的有效期 
Cookie的maxAge决定着Cookie的有效期,单位为秒(Second)。Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。如果maxAge属性为正数,则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的Cookie持久化,即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑,只要还在maxAge秒之前,登录网站时该Cookie仍然有效。如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。Cookie信息保存在浏览器内存中,因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为–1。如果maxAge为0,则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法,因此通过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除。

response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie。
删除时只需要把maxAge修改为0即可。
注意:
(1)从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。
maxAge属性只被浏览器用来判断Cookie是否过期。
(2)修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,
浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。

2.2 Cookie的域名

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

Cookie cookie = new Cookie("time","20080808"); // 新建Cookie
cookie.setDomain(".helloweenvsfei.com");           // 设置域名
cookie.setPath("/");                              // 设置路径
cookie.setMaxAge(Integer.MAX_VALUE);               // 设置有效期
response.addCookie(cookie);                       // 输出到客户端

可以修改本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个临时域名,然后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。
注意:domain参数必须以点(".")开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到客户端。

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

2.3 Cookie的路径

domain属性决定运行访问Cookie的域名,而path属性决定允许访问Cookie的路径(ContextPath)。例如,如果只允许/session/下的程序使用Cookie,可以这么写:

Cookie cookie = new Cookie("time","20080808");     // 新建Cookie
cookie.setPath("/session/");                          // 设置路径
response.addCookie(cookie);                           // 输出到客户端

设置为“/”时允许所有路径使用Cookie。path属性需要使用符号“/”结尾。name相同但path不相同的两个Cookie也是两个不同的Cookie。注意:页面只能获取它属于的Path的Cookie。例如/session/test/a.jsp不能获取到路径为/session/abc/的Cookie。

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

3、Cookie的字符编码

根据Cookie的规范,在Cookie中不能包含控制字符,仅能包含ASCII码为34~126的可见字符。因此如果字符不是ASCII码还要进行转换。
3.1 Unicode编码:保存中文

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

注意:Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码,因为浏览器不一定支持,而且JavaScript也不支持GBK编码。

3.2 BASE64编码:保存二进制文件
Cookie不仅可以使用ASCII字符与Unicode字符,还可以使用二进制数据。例如在Cookie中使用数字证书,提供安全度。使用二进制数据时也需要进行编码。

4、Cookie的压缩

Cookie在HTTP的头部,所以通常的gzip和deflate针对HTTPBody的压缩不能压缩Cookie,如果Cookie的量非常大,则可以考虑将Cookie也做压缩,压缩方式是将Cookie的多个k/v对看成普通的文本,做文本压缩。压缩算法可以使用gzip和deflate算法。
如下是对Cookie进行压缩和解压缩的代码:
//压缩  
private void compressCookie(Cookie c,HttpServletResponse res){  
    try{  
        ByteArrayOutputStream bos = null;  
        bos = new ByteArrayOutputStream();  
        DeflaterOutputStream dos = new DeflaterOutputStream(bos);   
        dos.write(c.getValue().getBytes());  
        dos.close();  
        System.out.println("before compress length:" + c.getValue().getBytes().length);  
        String compress = new sum.misc.BASE64Encoder().encode(bos.toByteArray());  
        res.addCookie(new Cookie("compress",compress));  
        System.out.println("after compress length:" + compress.getBytes().length);  
       }catch(IOException e){  
            e.printStackTrace();  
       }  
  
}  
//解压缩  
private void unCompressCookie(Cookie c){  
    try{  
       ByteArrayOutputStream out = new ByteArrayOutputStream();  
           byte[] compress = new sun.misc.BASE64Decoder().decodeBuffer(new String(c.getValue().getBytes()));  
       ByteArrayInputStream bis = new ByteArrayInputStream(compress);  
           InflaterInputStream inflater = new InflaterInputStream(bis);   
           byte[] b = new byte[1024];  
           int count;  
           while((count = inflater.read(b)) >= 0){  
               out.write(b,0,count);  
           }  
           inflater.close();  
           System.out.println(out.toByteArray());  
    }catch(Exception e){  
              e.printStackTrace();  
    }  
}  


5、Cookie是如何工作的?

可以用如下的方式来创建Cookie:
String getCookie(Cookie[] cookies,String key){
	if(cookies != null){
		for(Cookie cookie: cookies){
			if(cookie.getName().equals(key))
				return cookie.getValue();
		}
	}
	return null;
}

@Override
public void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException{
	Cookie[] cookies = request.getCookies();
	String username = getCookie(cookies,"username");
	String userAge = getCookie(cookies,"userAge");
	if(username == null)
		response.addCookie(new Cookie("username","aaa"));
		
	if(userAge == null)
		response.addCookie(new Cookie("userAge","17"));
	response.getHeaders("Set-Cookie");
}

Cookie是如何加到HTTP的Header中的呢?如下图是Tomcat创建Set-Cookie响应头的时序图:

真正构建Cookie是在org.apache.catalina.connector.Response类中完成的,调用generateCookieString方法将Cookie对象构造成一个字符串,构造的字符串格式如username=“aaa”;Version="1";Max-Age=1000。然后将这个字符串命名为Set-Cookie添加到MimeHeaders中。以上是服务端如何创建Cookie,那么如何从客户端获取Cookie呢?当我们请求一个URL路径时,浏览器会根据和这个URL路径将符合条件的Cookie放在Request请求头中传回给服务器,服务端通过request.getCookies()来取得所有Cookie。

二、理解Session

1、Session为什么出现?

Cookie可以让服务器程序跟踪每个客户端的访问,但每次客户端的访问都必须传回这些Cookie,如果Cookie很多,则增加了客户端与服务端的数据传输量,而Session的出现正是为了解决这个问题。
同一个客户端每次和服务端交互时,不需要每次都传回所有的Cookie值,而只要传回一个ID,这个ID是客户端第一次访问服务器时生成的,而每个客户端是唯一的,客户端只有传回这个ID就行了,这个ID通常是NAME为ISESIONID的一个Cookie。

2、Session是如何工作的?

2.1 取得Session ID
(1)基于URL Path Parameter
(2)基于Cookie,如果没有修改Context容器的Cookies标识,则默认也是支持的。
(3)基于SSL,默认不支持,只有connector.getAttribute("SSLEnabled")为True时才支持。
在第一种情况下,当浏览器不支持Cookie时,浏览器会将用户的SessionCookieName重写到用户请求的URL参数中,它的传递格式如/path/Servlet;name=value;name2=value2其中“Servlet”后面的K-V就是要传递的Path Parameters,服务器会从这个Path Parameters中拿到用户配置的SessionCookieName。如果在web.xml中配置session-config配置项,其cookie-config下的name属性就是这个SessionCookieName的值,如果没有配置这一项,默认就是“JSESSIONID”。
如果客户端支持Cookie,则tomcat仍然会解析Cookie中的Session ID,并覆盖URL中的Session ID。
如果是第三种情况,则会根据javax.servlet.request.ssl_session属性值设置Session ID。
2.2 创建HttpSession
当调用request.getSession()方法时,如果当前的Session ID还没有对应的HttpSession对象,那么就创建一个新的,并将这个对象加到org.apache.catalina.Manager的sessions容器保存。Manager类管理所以Session的生命周期,Session过期将被回收,服务器关闭,Session将被序列化到磁盘上。
如下是Session工作的时序图:



从时序图可看出,从Request中获取的Session对象保存在org,apache.catalina.Manager类中,它的实现类是org,apache.catalina.session.StandardManager,通过requestedSessionId从StandardManager的sessions集合中取出StandardSession对象。StandardManager类负责Servlet容器中所有的StandardSession对象的生命周期管理。当Servlet容器重启或关闭时,StandardManager负责持久化没有过期的StandardSession对象,它会将所有的StandardSession对象持久化到一个以“SESSIONID.ser“为文件名的文件中。当Servlet容器重启时,也就是StandardManager初始化时,它会重新读取这个文件,解析出所有Session对象,重新保存在StandardManager的sessions集合中。
注意:
(1) 为了获得更高的存取速度,服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此,Session里的信息应该尽量精简。
(2)只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session。
(3)为防止内存溢出,服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器,Session就自动失效了。


参考:博客http://blog.youkuaiyun.com/fangaoxin/article/details/6952954/    和     《深入分析Java Web技术内幕》
            



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值