会话技术—Cookie、Session详解

1. Cookie详解

1.1. 引言

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

web应用中的会话是指一个客户端浏览器和服务器之间连续发生的一系列请求和响应的过程。
web应用中的会话状态是指web服务器与浏览器在会话过程中产生的状态信息,借助会话状态,服务器能够把属于同一会话中的一系列的请求和响应过程关联起来。

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

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

1.2. 什么是cookie

由于HTTP是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理。
Cookie实际上是一小段的文本信息。
客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。

底层实现原理:web服务器通过在http响应消息头总增加Set-Cookie响应头字段将Cookie信息发送给浏览器,
浏览器则通过在http请求消息中增加Cookie请求头字段将Cookie回传给web服务器。

注意:Cookie功能需要浏览器的支持。
如果浏览器不支持Cookie(如大部分手机中的浏览器)或者把Cookie禁用了,Cookie功能就会失效。不同的浏览器采用不同的方式保存Cookie。

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

浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

1.3. cookie的读写

1.3.1. 添加cookie

Cookie cookie = new Cookie("name","tom");
//设置有效期,单位是秒;如果不设置,则默认为会话Cookie,浏览器关闭则销毁,并不保存到磁盘
cookie.setMaxAge(60*60);

//将cookie插入到一个Set-Cookie HTTP响应报头中
response.addCookie(cookie); 

第一次请求:
在这里插入图片描述
第二次请求:
在这里插入图片描述

1.3.2. 读取cookie

<%
    Cookie[] cookies =request.getCookies();
    if(cookies !=null && cookies.length>0){
        for(Cookie cookie:cookies){
            out.println(cookie.getName()+", "+cookie.getValue());
        }
    }else{
        out.println("cookie is null");
    }

    out.println("****************************");
    out.println(session);
    out.println(session.getId());
%>

第一次读取cookie时为null:
在这里插入图片描述
服务器创建session,将会将jessionid写入cookie。第二次请求时,则有了该cookie:
在这里插入图片描述
客户端的cookie文件可以存储若干个cookie对象的信息,在读取时,reques.getCookies()返回一个Cookie的数组,可在该数组中遍历寻找指定的Cookie对象。
Cookie并不提供修改、删除操作。
如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。

如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。

注意是0而不是负数。负数代表其他的意义。

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

1.4. cookie的生命周期

又称为有效期,生存时间,过期时间等。

cookie的内容主要包括:名字,值,过期时间,路径和域。路径与域一起构成cookie的作用范围。
两种方式设置cookie的过期时间:
① expires(未来某个时刻过期):

HttpCookie cookie=new HttpCookie("UserName");  
cookie.Expires=DateTime.MinValue;
//表示关闭浏览器UserName过期  
response.addCookie(cookie);  

② max-age(经过一定秒数过期)

Cookie cookie = new Cookie("username","tom");
//设置有效期,单位是秒
cookie.setMaxAge(60*60);

若不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,关闭浏览器窗口,cookie就消失。这种生命期为浏览器会话期的cookie被称为会话cookie。
会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。
若设置了过期时间,浏览器就会把cookie保存到硬盘上(持久化cookie),关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。
在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。

cookie.setMaxAge设置为0时,会马上在浏览器上删除指定的cookie

cookie.setMaxAge设置为-1时,代表关闭当前浏览器即失效。

1.5. cookie的domain

Cookie是不可跨域名的。

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

正常情况下,同一个一级域名下的两个二级域名如www.hello.com和images.hello.com也不能交互使用Cookie,因为二者的域名并不严格相同。

如果想所有hello.com名下的二级域名都可以使用该Cookie,需要设置Cookie的domain参数,例如:

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

注意:domain参数必须以点(“.”)开始。另外,name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie,可以生成两个Cookie,domain属性分别为两个域名,输出到客户端。

1.6. cookie的path

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

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

设置为“/”时允许所有路径使用Cookie。path属性需要使用符号“/”结尾。name相同但path不同的两个Cookie也是两个不同的Cookie。

1.7. cookie常用的API

//用于在其响应头中增加一个相应的Set-Cookie头字段
addCookie

//用于获取客户端提交的Cookie
GetCookie
public Cookie(String name,String value)
   
//该方法设置与 cookie 关联的值。
setValue

//该方法获取与 cookie 关联的值。
getValue

//该方法设置 cookie 过期的时间(以秒为单位)。如果不这样设置,cookie只会在当前 session 会话中持续有效。
setMaxAge

//该方法返回 cookie 的最大生存周期(以秒为单位),默认情况下,-1 表示 cookie 将持续下去,直到浏览器关闭
getMaxAge

//该方法设置 cookie 适用的路径。如果您不指定路径,与当前页面相同目录下的(包括子目录下的)所有 URL 都会返回 cookie。
setPath

//该方法获取 cookie 适用的路径。
getPath

//该方法设置 cookie 适用的域
setDomain

//该方法获取 cookie 适用的域
getDomain

2. session详解

2.1. 概述

Session是另一种记录浏览器状态的机制,Cookie保存在浏览器中,Session保存在服务器中。用户使用浏览器访问服务器的时候,服务把用户的信息,以某种形式记录在服务器,这就是Session
为何使用Session因为Session可以存储对象,Cookie只能存储字符串可以解决很多Cookie解决不了的问题
功能作用:服务器用于共享数据技术

2.2. session原理分析

首先浏览器请求服务器访问web站点时,程序需要为客户端的请求创建一个session的时候,服务器首先会检查这个客户端请求是否已经包含了一个session标识、称为SESSIONID,如果已经包含了一个sessionid则说明以前已经为此客户端创建过session,服务器就按照sessionid把这个session检索出来使用,如果客户端请求不包含session id,则服务器为此客户端创建一个session并且生成一个与此session相关联的session id,sessionid 的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个sessionid将在本次响应中返回到客户端保存,保存这个sessionid的方式就可以是cookie,这样在交互的过程中,浏览器可以自动的按照规则把这个标识发回给服务器,服务器根据这个sessionid就可以找得到对应的session,又回到了这段文字的开始。

3.3. session常用API

  1. 获取session
request.getSession();  //如果没有将创建一个新的,等效getSession(true);
request.getSession(boolean);  //true:没有将创建,false:没有将返回null
  1. 属性操作
// 用来存放一些信息,然后才能共享信息 
setAttrubute(key,value);
getAttribute(key);
  1. 设置session有效时间
session.invalidate() //将session对象销毁
setMaxInactiveInterval(int interval) //设置有效时间,单位秒

3.4. session的生命周期

常常听到这样一种误解“只要关闭浏览器,session就消失了”。其实可以想象一下会员卡的例子,除非顾客主动对店家提出销卡,否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的,除非程序通知服务器删除一个session,否则服务器会一直保留,程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭,因此服务器根本不会有机会知道浏览器已经关闭,之所以会有这种错觉,是大部分session机制都使用会话cookie来保存session id,而关闭浏览器后这个session id就消失了,再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的HTTP请求头,把原来的session id发送给服务器,则再次打开浏览器仍然能够找到原来的session

恰恰是由于关闭浏览器不会导致session被删除,迫使服务器为seesion设置了一个失效时间,一般是30分钟,当距离客户端上一次使用session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把session删除以节省存储空间。

我们也可以自己来控制session的有效时间

  1. session.invalidate() 将session对象销毁

  2. setMaxInactiveInterval(int interval) 设置有效时间,单位秒

  3. 在web.xml中配置session的有效时间

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

讨论了这么久,session的生命周期就是:

创建:第一次调用getSession()

销毁
1、超时,默认30分钟
2、执行api:session.invalidate()将session对象销毁、setMaxInactiveInterval(int interval) 设置有效时间,单位秒
3、服务器非正常关闭自杀,直接将JVM马上关闭

如果正常关闭,session就会被持久化,也就是钝化(写入到文件中,因为session默认的超时时间为30分钟,正常关闭后,就会将session持久化,等30分钟后,就会被删除)

session ID的URL重写

当浏览器将cookie禁用,基于cookie的session将不能正常工作,每次使用request.getSession() 都将创建一个新的session。达不到session共享数据的目的,但是我们知道原理,只需要将session id 传递给服务器session就可以正常工作的。

解决:通过URL将session id 传递给服务器:URL重写

  1. 手动方式: url;jsessionid=…
  2. api方式:
    encodeURL(java.lang.String url) 进行所有URL重写
    encodeRedirectURL(java.lang.String url) 进行重定向 URL重写

这两个用法基本一致,只不过考虑特殊情况,要访问的链接可能会被Redirect到其他servlet去进行处理,这样你用上述方法带来的session的id信息不能被同时传送到其他servlet.这时候用encodeRedirectURL()方法就可以了

如果浏览器禁用cooke,api将自动追加session id ,如果没有禁用,api将不进行任何修改。
注意:如果浏览器禁用cookie,web项目的所有url都需进行重写。否则session将不能正常工作。

当禁止了cookie时
在这里插入图片描述

3. 总结

  1. cookie与session
    cookie是一种在客户端记录用户信息的技术,因为http协议是无状态的,为了解决这个问题而产生了cookie。记录用户名等一些应用。
  2. 工作原理
    session是一种在服务端记录用户信息的技术,一般session用来在服务器端共享数据。
    cookie是由服务器端创建发送回浏览器端的,并且每次请求服务器都会将cookie带过去,以便服务器知道该用户是哪一个。其cookie中是使用键值对来存储信息的,并且一个cookie只能存储一个键值对。所以在获取cookie时,是会获取到所有的cookie,然后从其中遍历。在这里插入图片描述
    session的工作原理就是依靠cookie来做支撑,第一次使用request.getSession()时session被创建,并且会为该session创建一个独一无二的sessionid存放到cookie中,然后发送会浏览器端,浏览器端每次请求时,都会带着这个sessionid,服务器就会认识该sessionid,知道了sessionid就找得到哪个session。以此来达到共享数据的目的。 这里需要注意的是,session不会随着浏览器的关闭而死亡,而是等待超时时间。

文章链接:https://www.cnblogs.com/whgk/p/6422391.html

4. token令牌防止表单重复提交

要想使用token令牌机制防止表单重复提交,采用的主要是session会话技术,在jsp显示界面生成一个随机的不重复的值,可以使用java.util.UUID.randomUUID();方法随机生成。
生成的值通过session.setAttribute(key,value);方法保存到会话作用域中,其中,在提交的form表单中也将此值隐藏提交,在servlet中同时获取session中的值以及form表单中的请求对象,进行比较判断是否提交的同一次表单。最后在转发到其他页面时将session作用域中的值remove。

  1. jsp页面
<body>
<%
	//产生一个唯一不重复的令牌
	String uuid = UUID.randomUUID().toString();
	session.setAttribute("token", uuid);
%>
<form action="UserServlet" method="post">
	用户名:<input type="text" name="username"/>
	<input type="hidden" name="token" value="<%=uuid%>"/> 
	<input type="submit" value="注册" id="subId"/>
</form>
</body>
  1. servlet处理
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//在servlet中应该有一个一模一样的令牌,这样每次提交来的数据会带上令牌,我们服务器
		//可以验证和我本地的令牌是否一致
		HttpSession session = request.getSession();
		//从session中获取令牌,服务器从session中取出的令牌
		String token = (String) session.getAttribute("token");
		
		//再从页面取出令牌对照一下
		String pageToken = request.getParameter("token");
		
		System.out.println("服务器端取到的令牌:"+token);
		System.out.println("页面带过来的令牌:"+pageToken);
		//如果在这里把session中的令牌变化,直接将服务器中的令牌移除。第二次请求过来,
		//服务器中的token没值,页面会带来缓存的token
		session.removeAttribute("token");
		//对照令牌,如果一致,提交请求,否则不提交
		if(pageToken.equals(token)){
			//获取用户名
			String username = request.getParameter("username");
			//保存到数据
			System.out.println(username+"已经保存到数据库");
			//转发到页面
			request.getRequestDispatcher("/success.jsp").forward(request, response);
			//response.sendRedirect(request.getContextPath()+"/success.jsp");
		}else{
			response.sendRedirect(request.getContextPath()+"/error.jsp");
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值