4.会话技术

一. 会话技术

1. 什么是会话?

会话可简单理解为:
	用户开一个浏览器,点击多个超链接,访问服务器多个web资源,
	然后关闭浏览器,整个过程称之为一个会话。

2. 会话过程中要解决的一些问题?

	每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。
	例如:
		用户点击超链接通过一个servlet购买了一个商品,程序应该想办法保存用户购买的商品,
		以便于用户点结帐servlet时,结帐servlet可以得到用户购买的商品为用户结帐。

	思考:用户购买的商品保存在request或servletContext中行不行?
		request --> 作用域的范围太小(每次选择商品存一下,下次请求又不知道存了啥)
		servletContext --> 作用域的范围太大(选择商品存一下,多个用户的数据混乱了)

3. 保存会话数据的两种技术:

	Cookie(小饼干)
		Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。
		当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。
		这样,web资源处理的就是用户各自的数据了。

	Session
		Session是服务器端技术,利用这个技术,
		服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,
		由于session为用户浏览器独享,所以用户在访问服务器的web资源时,
		可以把各自的数据放在各自的session中,
		当用户再去访问服务器中的其它web资源时,
		其它web资源再从用户各自的session中取出数据为用户服务。
		(--> 不同的浏览器会存在服务器的不同位置上)


二. Cookie技术

1. Cookie的简单入门

	response.setHeader("Set-Cookie", "name=bingxiang");
--> 这样拼接头信息太麻烦,Sun公司专门提供了一套API,便于Cookie的处理。

	Date date = new Date();
	Cookie c = new Cookie("lastTime",date.getTime()+""); 
	c.setMaxAge(3600*24*30);
	c.setPath(request.getContextPath());// --> 这个是给浏览器用的, 所以要给上web应用名称。
	//c.setDomain(".baidu.com");// --> 设置之后, 浏览器会拒绝接收该Cookie.
	response.addCookie(c);
	
	浏览器最终拼接的信息 --> Cookie:name=bingxiang; lastTime=1517844967488

2. 遍历Cookie的方法(注意判断非空)

	response.setContentType("text/html;charset=utf-8");
	Cookie[] cs = request.getCookies();
	Cookie findC = null;
	if(cs != null){ // --> 这句话很重要
		for(Cookie c: cs){
			if("lastTime".equals(c.getName())){
				findC = c;
			}
		}
	}
	
	if(findC==null){
		response.getWriter().write("您是第一次访问本网站");
	}else{
		String lastTime = findC.getValue();
		response.getWriter().write("您上次访问本网站的时间是: " + lastTime);
	}

3. Cookie的详细介绍

	Cookie是基于set-Cookie响应头和Cookie请求头工作的,
	服务器可以发送set-Cookie请求头命令浏览器保存一个cookie信息,
	浏览器会在访问服务器时以Cookie请求头的方式带回之前保存的信息

	request.getCookies();
    response.addCookie(Cookie c);

    // --> Cookie在构造的时候就需要设定好cookie的名字和值
    new  Cookie(String name,String value);

	Cookie对象的方法: 
    --> 1. getName();
    --> 2. getValue();
    --> 3. setValue();

	--> 4. setMaxAge与getMaxAge方法  
        --  一个Cookie如果没有设置过MaxAge则这个Cookie是一个会话级别的Cookie,
			这个Cookie信息打给浏览器后浏览器会将它保存在浏览器的内存中,
			这意味着只要浏览器已关闭随着浏览器内存的销毁Cookie信息也就消失了.

		--  一个Cookie也可以设置MaxAge,浏览一一旦发现收到的Cookie被设置了MaxAge,
			则会将这个Cookie信息以文件的形式保存在浏览器的临时文件夹中(不再是),
			保存到指定的时间到来位置.这样一来即使多次开关浏览器,
			由于这些浏览器都能在临时文件夹中看到cookie文件,
			所以在cookie失效之前cookie信息都存在.

        --  想要命令浏览器删除一个Cookie,
			发送一个同名同path的cookie,maxage设置为0,
			浏览器以'名字+path'识别cookie,
			发现同名同path,cookie覆盖后立即超时被删除,从而就删除了cookie.
        
    --> 5. setPath与getPath方法
        --  用来通知浏览器在访问服务器中的哪个路径及其子路径时带着当前cookie信息过来, 
            如果不明确设置,则默认的路径是发送Cookie的Servlet所在的路径。
            http://localhost/Day05/servlet/...(而不是http://localhost/Day05/servlet/ServletDemo1)
	

    --> 6. setDomain与getDomain方法
        --  用来通知浏览器在访问哪个域名的时候带着当前的cookie信息.
			但是要注意,
			现代的浏览器一旦发现cookie设置过domain信息, 
			则会拒绝接受这个Cookie.我们平常不要设置这个方法

4. Cookie的具体案例

		---  显示用户上次浏览过的书。

	1. Book的javaBean类
	2. BookDao的数据接口类。


	3. ServletDemo2 --> 列出当前看过书的Servlet
		具体代码: 
			response.setContentType("text/html;charset=utf-8");
			//1. 查询数据库中所有的书, 进行展示
			Map<String,Book> map = BookDao.getBooks();
			for(Map.Entry<String , Book> entry : map.entrySet()){
				Book book = entry.getValue();
				response.getWriter().write("<a href='"+request.getContextPath()+"/servlet/ServletDemo3?id="+book.getId()+"'>"+book.getName()+"</a><br>");
			}
			response.getWriter().write("<hr>");
			
			//2.显示之前看过的书
			Cookie [] cs = request.getCookies();
			Cookie findC = null;
			if(cs!=null){
				for(Cookie c : cs){
					if("last".equals(c.getName())){
						findC = c;
					}
				}
			}
			if(findC == null){
				response.getWriter().write("没有看过任何书!");
			}else{
				response.getWriter().write("您曾经浏览过的书:<br>");
				String[] ids = findC.getValue().split(",");
				for(String id : ids){
					Book book = BookDao.getBook(id);
					response.getWriter().write(book.getName()+"<br>");
				}
			}


		4. ServletDemo3 --> 展示选中书的详细信息,并把当前书的id纪录到cookie中
			具体代码: 
				response.setContentType("text/html;charset=utf-8");
				//1.获取要看的书的id,查询数据库找出书,输出书的详细信息
				String id = request.getParameter("id");
				Book book = BookDao.getBook(id);
				if(book==null){
					response.getWriter().write("找不到这本书!");
					return;
				}else{
					response.getWriter().write("<h1>书名:"+book.getName()+"</h1>");
					response.getWriter().write("<h3>作者:"+book.getAuth()+"</h3>");
					response.getWriter().write("<h3>售价:"+book.getPrice()+"</h3>");
					response.getWriter().write("<h3>出版社:"+book.getPublish()+"</h3>");
					response.getWriter().write("<h3>描述信息:"+book.getDescription()+"</h3>");
				}
				
				//2.发送cookie保存最后看过的书
				// 什么都没看过 --- 1 --> 1
				// 看过1 -- 2,1 --> 2,1
				// 看过2,1--3,2,1 --> 3,2,1
				// 看过3,2,1 -- 4,3,2 --> 4,3,2
				// 看过4,3,2 --3,4,2 --> 3,4,2
				String ids = "";
				
				Cookie [] cs = request.getCookies();
				Cookie findC = null;
				if(cs!=null){
					for(Cookie c : cs){
						if("last".equals(c.getName())){
							findC = c;
						}
					}
				}
				
				// ---> 算法
				// 当前看的直接加到结果中,
				// 再遍历之前的cookie,
				// 如果历史纪录中的第一个和当前没有重复就添加到结果的后面,如果一样就不添加了
				// 因为每次都是添加一个,所以只要和当前那本书不一样都可以添加,
				if(findC == null){
					//说明之前没有看过书的记录
					ids += book.getId();
				}else{
					//说明之前有历史看过的书的记录,需要根据历史记录算一个新的记录出来
					String [] olds = findC.getValue().split(","); 
					StringBuffer buffer = new StringBuffer();
					buffer.append(book.getId()+","); // --> 添加当前看的书
					for(int i = 0;i<olds.length && buffer.toString().split(",").length<3 ;i++){
						String old = olds[i];
						if(!old.equals(book.getId())){
							buffer.append(old+",");
						}
					}
					ids = buffer.substring(0, buffer.length()-1); // --> 删除第一个','号
				}
				
				Cookie lastC = new Cookie("last",ids);
				lastC.setMaxAge(3600*24*30);
				lastC.setPath(request.getContextPath());
				response.addCookie(lastC);

5. Cookie的细节

	1. 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
	 
	2. 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie
	   浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

	4. 如果创建了一个cookie,并将他发送到浏览器,
	   默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。
	   若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。
	   将最大时效设为0则是命令浏览器删除该cookie。

	   注意,删除cookie时,path必须一致,否则不会删除(浏览器通过cookie的name+path来标识一个cookie)

	5. 浏览器会用domain+path+name区分一个cookie(并且拒收第三方cookie)。而服务器以对象来区分cookie。

三. Session技术

1. session概述

	1. 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),
	   注意:一个浏览器独占一个session对象(默认情况下)。
	   因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session(内存空间)中,
	   当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。

	2. session是一个域对象,作用范围为整个会话。


	3. Session和Cookie的主要区别在于:
		Cookie是把用户的数据写给用户的浏览器。
		Session技术把用户的数据写到用户独占的session中。

2. session的生命周期

	1. Session对象由服务器创建,
	   开发人员可以调用request对象的getSession()方法得到session对象,
	   如果服务器发现内存中没有该浏览器对应的session就会创建该session并返回,
	   如果已经存在则直接返回已有的session。

	2. session并不会一直存在在内存中,
	   经过一段时间如果无人使用session将会被销毁,
	   一般这个时间为30分钟,
	   可以在web.xml中配置<session-config>设置该时间值。

	3. 调用session的invalidate()也可以销毁session。

	4. 如果服务器是正常关闭,还未超时的session会被以文件的形式保存在服务器的work目录下,这个过程叫做session的钝化.
	   下次再正常启动服务器时,钝化着的session会被恢复到内存中,这个过程叫做session的活化.

	5. 作用:在会话范围内共享数据

3. session实现原理

	服务器是如何实现一个session为一个用户浏览器服务的?
		--> Session是基于Cookie的
		--> 名字为JSESSIONID的Cookie的值是Session的唯一标识。

4. Session的案例(购物)

	1. ServletDemo4 --> 购物车的Servlet
		具体代码:
			
			String prod = request.getParameter("prod");
			prod = new String(prod.getBytes("iso8859-1"),"utf-8");
	
			//为了保证下次打开浏览器的时候还知道购物车里面的商品,
			//那么就要重新设置session对应的Cookie的最大生命范围,
			//request.getSession().setAttribute("prod", prod);// 这样只能适用于一次会话范围

			HttpSession session = request.getSession();
			Cookie jc = new Cookie("JSESSIONID",session.getId()); // --> 特殊名字的Cookie
			jc.setPath(request.getContextPath()); // --> 当前Web应用的名称
			jc.setMaxAge(1800); // --> 30分钟就足够了
			response.addCookie(jc);
			session.setAttribute("prod", prod); // --> 存数据

	2. ServletDemo5 --> 支付的Servlet
		具体代码:
			response.setContentType("text/html;charset=utf-8");
			HttpSession session = request.getSession();
			String prod = (String) session.getAttribute("prod");
			response.getWriter().write("您购买的是"+prod+"价值99999999999元");


	3. prodList.jsp --> 商品页面
		具体代码: 
			<%@ page language="java" import="java.util.*" pageEncoding="utf-8" session="false"%>
			<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
			<html>
			  <head>
			  </head>
			  <body>
			  <%
			  	request.getSession();
			  
			  	String url1 = request.getContextPath()+"/servlet/ServletDemo4?prod=电视机";
			  	url1 = response.encodeURL(url1);
			  	String url2 = request.getContextPath()+"/servlet/ServletDemo4?prod=冰箱";
			  	url2 = response.encodeURL(url2);
			  	String url3 = request.getContextPath()+"/servlet/ServletDemo5";
			  	url3 = response.encodeURL(url3);
			   %>
			  	<a href="<%= url1 %>">电视机</a>
			  	<a href="<%= url2 %>">冰箱</a>
			  	<a href="<%= url3 %>">结账</a>
			  </body>
			</html>

5. session 的原理:

	request.getSession()方法会检查请求中有没有名字为'JSESSIONID'这个cookie,
	如果没有则检查请求的URL后有没有以参数的形式带着JSESSIONID过来,
	如果还找不到则认为这个浏览器没有对应的Session,
	创建一个Session然后再在响应中添加JSESSIONID cookie,值就是这个Session的id。

	默认情况下,
	JSESSIONID 的path为当前web应用的名称,并且没有设置过MaxAge,是一个会话级别的cookie.
	这意味着一旦关闭浏览器再新开浏览器时,由于JSESSIONID丢失,会找不到之前的Session。
	我们可以手动的发送JSESSIONID cookie,名字和path设置的和自动发送时一样,
	但是设置一下MaxAge,使浏览器除了在内存中保存JSESSIONID信息以外还在临时文件夹中以文件的形式保存,
	这样即使重开浏览器仍然可以使用之前的session.

6. URL重写 ( 一般不常用 )

	(如果用户设置了不接收Cookie,导致应该存储在Cookie的数据没有保存)
	(可以使用URL重写解决这个问题)
	
	由于没有了JSESSIONID这个Cookie,导致现在用户的浏览器被标识出来了。
	让网站上所有的超链接都拼接上JSESSIONID.

	SUN公司提供了两个简单的方法: 
		1. response.encodeRedirectURL(java.lang.String url) 
			--> 用于对sendRedirect(重定向)方法后的url地址进行重写。
		2. response.encodeURL(java.lang.String url)
			--> 用于对表单action和超链接的url地址进行重写 

	例如:
		request.getSession(); --> 必须先获取Session,不然都没有创建,那就取不到JSESSIONID。
		String url3 = request.getContextPath()+"/servlet/ServletDemo5";
		url3 = response.encodeURL(url3);

		经过URL重写后变成了: 
			url3 --> /ServletDemo/servlet/ServletDemo5;jsessionid=E047C1D781EB0DD09172B13598A4FBE2

	注意:
		request.getSession() --在URL重写之前一定要先创建出Session,才有Session id,才能进行重写
		response.encodeURL()--- 一般的地址都用这个方法重写
		response.encodeRedirectURL() --- 如果地址是用来进行重定向的则使用这个方法

		url重写的方法一旦发现浏览器带回了任意cookie信息,
		则认为客户端没有禁用cookie, 就不会再进行重写操作.

7. Session案例:

7.1 用户登录注销

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CYv0hrnG-1619937127827)(img2/4_3.png)]
request.getSession(false) --> 传入了一个参数
–> 这样如果之前有Session就能返回这个Session
–> 没有的话就返回null

7.2 防止表单重复提交
	前台校验 --> 是给用户一个友好的体验
	后台检验 --> 才是真正的数据校验

	1. 注册页面代码: 
		  <%
		  	// http://localhost:8080/ServletDemo/resubmit/index.jsp
		  	Random r = new Random();
		  	int valinum = r.nextInt();
		  	session.setAttribute("valinum",valinum+"");
		  	System.out.println("jsp_valinum: " + valinum);
		   %>
		  	<form action="${pageContext.request.contextPath }/servlet/ResubServlet" method="POST">
		  		用户名:<input type="text" name="username"/>
		  		<input type="hidden" name="valinum" value="<%=valinum %>"/>
		  		
		  		<input type="submit" value="注册"/>
		  	</form>

	2. 后台校验代码: 
			request.setCharacterEncoding("utf-8");
			response.setContentType("text/html;charset=utf-8");
			try {
				Thread.sleep(4*1000); // 模拟网络延迟
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			String username = request.getParameter("username");
			//获取传过来的验证随机数
			String valinum = request.getParameter("valinum"); 
			//获取存在Session中存储的验证随机数
			String valinum2 = (String) request.getSession().getAttribute("valinum"); 
			// 表单提交两次得到的验证随机数应该是一样的
			// 但是注册一次后,就将Session中的验证数变为了null
			// 此时两个验证随机数数不一致了,就说明重复注册了。
			System.out.println("param_valinum: " + valinum);
			System.out.println("session_valinum2: " + valinum2);
			if(valinum2 != null && !"".equals(valinum2) && valinum.equals(valinum2)){
				request.getSession().removeAttribute("valinum"); // --> 将验证随机数清除
				System.out.println("向数据库中注册一次:"+username);
			}else{
				response.getWriter().write("from web:不要重复提交!!");
			}
7.3 实现一次性验证码

四. Cookie和Session总结

	cookie是客户端技术
		数据保存在客户端, 这个信息可以保存很长时间
		数据随时有可能被清空, 所以cookie保存的数据是不太靠谱的
		数据被保存在了客户端, 随时有可能被人看走,
		如果将一些敏感信息比如用户名密码等信息存在cookie中, 可能有安全问题。

	session是服务器端技术
		数据保存在服务区端, 相对来说比较稳定和安全,
		占用服务器内存, 所以一般存活的时间不会太长,超过超时时间就会被销毁.
		我们要根据服务器的压力和session的使用情况合理设置session的超时时间,
		既能保证session的存活时间够用, 同时不用的session可以及时销毁减少对服务器内存的占用.

备注

1. 注意:即使禁止了Cookie,浏览器对localhost是不阻止的,要想试验就要用127.0.0.1进行。

2. response.encodeURL(url3); --> 这个方法非常的智能
	 --> 他会检查服务器有没有带JSESSIONID这个Cookie,
	 --> 如果带了就不重新URL,没带JSESSIONID就重写一下。


3. Session什么时候会销毁?
	1. --> 30分钟到了
	2. --> 调用session的invalidate()方法
	3. --> 服务器非正常关闭,没保存上。(正常关闭服务器,Cookie会被保存成文件)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值