八、会话信息cookie和session

会话


什么是会话

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

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

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

保存会话数据的两种技术


Cookie

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

Session

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

Cookie技术


八、会话信息cookie和session_第1张图片

Cookie API

  • javax.servlet.http.Cookie类用于创建一个Cookie,response接口中也定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。Cookie类的方法:
    1.public Cookie(String name,String value)
    2.setValue与getValue方法
    3.setMaxAge与getMaxAge方法
    4.setPath与getPath方法 /day06
    5.setDomain与getDomain方法
    6.getName方法

Cookie 细节

  • 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
  • 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
  • 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
  • 如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。
  • 注意,删除cookie时,path必须一致,否则不会删除

Cookie 应用

  • 显示用户上次访问时间
		PrintWriter  out = response.getWriter();
		out.write("您上次访问时间是:");
		//1.获取用户上次访问的时间,显示
		Cookie cookies[] = request.getCookies();
		for(int i=0;cookies!=null && i<cookies.length;i++){
			Cookie cookie = cookies[i];
			if(cookie.getName().equals("lastAccessTime")){
				long time = Long.parseLong(cookie.getValue());
				Date date = new Date(time);
				out.write(date.toLocaleString());
			}
		}
		
		//2.把本次的时间以cookie的形式回写给客户机   (lastAccessTime)
		Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
		cookie.setMaxAge(7*24*60*60);
		response.addCookie(cookie);
  • 显示用户上次浏览过的商品
    八、会话信息cookie和session_第2张图片

商品列表servlet,显示网站商品列表,并根据浏览器带过来的cookie显示用户历史浏览记录

		PrintWriter  out = response.getWriter();
		//1.显示网站所有商品
		out.print("本网站有如下书籍:
"
); Map<String,Book> map = DB.getMap(); for(Map.Entry<String, Book> entry : map.entrySet()){ Book book = entry.getValue(); out.print(""+book.getName()+"
"); } out.print("您曾经看过如下商品:
"
); //2.显示用户曾经浏览过的商品 // bookHistory Cookie cookie = null; Cookie cookies[] = request.getCookies(); for(int i=0;cookies!=null && i<cookies.length;i++){ if(cookies[i].getName().equals("bookHistory")){ cookie = cookies[i]; } } if(cookie!=null){ //找到了bookHistory这个cookie String bookHistory = cookie.getValue(); //4_6_1 String ids[] = bookHistory.split("\\_"); for(String id: ids){ Book book = (Book) DB.getMap().get(id); out.print(book.getName() + "
"
); } }

商品详情servlet,根据request携带的商品id显示商品详情,并且将这个id放入用户浏览历史的cookie中

        PrintWriter  out = response.getWriter();
		//1.根据用户带过来的id值,显示相应商品的信息
		out.print("您想看的书的详细信息为:
"
); String id = request.getParameter("id"); Book book = (Book) DB.getMap().get(id); out.print(book.getId() + "
"
); out.print(book.getName() + "
"
); out.print(book.getAuthor() + "
"
); //2.以cookie的形式回写该商品的id号给浏览器 String bookHistory = makeCookie(book.getId(),request); Cookie cookie = new Cookie("bookHistory",bookHistory); cookie.setMaxAge(10000); response.addCookie(cookie);

根据用户本次浏览的商品信息构建代表用户浏览量历史的cookie

    //根据用户原来看过的书,以及现在看的书的id,构建新的cookie值
	private String makeCookie(String id, HttpServletRequest request) {

		//1.得到用户曾经看过的书
		String bookHistory = null;
		Cookie cookies[] = request.getCookies();
		for(int i=0;cookies!=null && i<cookies.length;i++){
			if(cookies[i].getName().equals("bookHistory")){
				bookHistory = cookies[i].getValue();
			}
		}
		
		if(bookHistory==null){
			bookHistory = id;
			return bookHistory;
		}

		//bookHistory=1_2_5    代表用户曾经看一些书,接着程序要得到用户曾经看过什么书
		String ids[] = bookHistory.split("_");
		//为了检测数组中是否包含当前id,我们应该把数据转成集合,并且还要转成链表结构的集合
		LinkedList<String> idList = new LinkedList(Arrays.asList(ids));
		if(idList.contains(id)){
			idList.remove(id);
		}else{
			if(idList.size()>=3){
				idList.removeLast();
			}
		}
		idList.addFirst(id);
		StringBuffer sb = new StringBuffer();
		for(String lid: idList){   //1_2_3_
			sb.append(lid + "_");
		}
		
		return sb.deleteCharAt(sb.length()-1).toString();
	}

Session技术


八、会话信息cookie和session_第3张图片
Session 介绍

  • 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
  • Session和Cookie的主要区别在于:
    1.Cookie是把用户的数据写给用户的浏览器。
    2.Session技术把用户的数据写到用户独占的session中。
  • Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。

session实现原理

八、会话信息cookie和session_第4张图片

上图中session是通过cookie实现的,浏览器每次访问服务器时在cookie中携带Jsessionid,服务器根据Jsessionid去获取该浏览器对应的session。

禁用Cookie后的session实现原理

  • 浏览器禁用cookie后,为了保证session的正常使用,服务器端重写网页上所有的URL地址,使得网页上所有URL地址后面带上用户的sessionid,重写后的URL地址类似于www.baidu.com;jsessionid=1354646546。request.getSession方法内部会从用户携带的cookie和url地址中查找用户的sessionid,返回用户的session。

  • URL重写api
    1.response. encodeRedirectURL(java.lang.String url)
    用于对sendRedirect方法后的url地址进行重写。
    2.response. encodeURL(java.lang.String url)
    用于对表单action和超链接的url地址进行重写

  • 示例主页的两个链接都被重写,因此即使浏览器禁用cookie,该示例功能也不受影响
    1.WelcomeServlet重写网页url

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out=response.getWriter();
		request.getSession();
		String url1=response.encodeURL("/day07/SessionDemo1");
		String url2=response.encodeURL("/day07/SessionDemo2");
		out.print("把洗衣机加入购物车    ");
		out.print("购买  ");
	}

2.用户点击“把洗衣机加入购物车”访问SessionDemo1,因为用户访问的url被重写成day07/SessionDemo1;jesessionid=xxxxx,因此即使浏览器禁用cookie,SessionDemo1也能取得用户的session

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// 浏览器的请求第一次访问到这行代码的时候,服务器为该浏览器创建session对象
		HttpSession session=request.getSession();
		String sessionid=session.getId();
		session.setAttribute("name", "洗衣机");
	}

3.用户点击“购买”访问SessionDemo2,因为用户访问的url被重写成day07/SessionDemo2;jesessionid=xxxxx,因此即使浏览器禁用cookie,SessionDemo2也能取得用户的session

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out=response.getWriter();
		HttpSession session=request.getSession();
		String product=(String) session.getAttribute("name");
		out.write("您购买的商品是:"+product);
		
	}

session应用

  • 利用Session防止表单重复提交
    以下示例既在前端防用户重复提交,提升用户体验,又在后端防重复提交,彻底规避了用户不规范操作(例如刷新、单机后退再次提交)引起的重复提交。
    第一步:浏览器请求访问表单
    第二步:服务器生成唯一令牌,并随表单隐藏域一起返回给浏览器,同时服务器保存该令牌
    第三步:用户填写表单,点击提交,提交内容包括用户填写的内容和隐藏域中的表单令牌
    第四步:服务器收到用户提交,检验令牌是否存在,如果存在,处理请求并移除令牌。如果令牌不存在,证明表单已经提交过,提示不能重复提交。

FormServlet生成令牌并保存在用户session中,将请求转发给form.jsp

public class FormServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;  
    public FormServlet() {
        super();
    }
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	
		 //产生令牌
		TokenProcessor tp=TokenProcessor.getInstance();
		String token=tp.generateToken();
		//将令牌保存在session中
		request.getSession().setAttribute("token", token);
		//转发请求到form.jsp
		request.getRequestDispatcher("form.jsp").forward(request, response);
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

class TokenProcessor{   //令牌
	private TokenProcessor(){
		super();
	}
	private static final TokenProcessor instance=new TokenProcessor();
	public static TokenProcessor getInstance(){
		return instance;
	}
	
	public String generateToken() {
		//产生随机数
		String token=System.currentTimeMillis()+new Random().nextInt()+"";
		//产生数据指纹(摘要)
		try {
			MessageDigest md = MessageDigest.getInstance("md5");
			byte[] md5=md.digest(token.getBytes());
			//base64编码
			BASE64Encoder encoder=new BASE64Encoder();
			return encoder.encode(md5);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}
	}
}

form.jsp负责显示带有令牌的表单

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title heretitle>
 <script type="text/javascript">
 	function doSubmit(){
	  var button=document.getElementById("submit");
	  button.disabled='disabled';
	  return true;
 	}
 script>
head>
<body>
	<form action="/day07/DoFormServlet" method="post">
		<input type="hidden" name="token" value="${token}" onsubmit="return doSubmit()">
		用户名:<input type="text" name="username">
		<input type="submit" value="提交">
	form>
body>
html>

表单提交后由DoFormServlet来处理

public class DoFormServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
    public DoFormServlet() {
        super();
    }
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		boolean b = isTokenValid(request);
		if(!b){
			System.out.println("请不要重复提交");
			return;
		}
		request.getSession().removeAttribute("token");
		System.out.println("向数据库中注册一个用户");
		
	}

	//判断表单号是否有效
	private boolean isTokenValid(HttpServletRequest request) {
		String client_token=request.getParameter("token");
		if(client_token==null){
			return false;
		}
		String server_token=(String) request.getSession(false).getAttribute("token");
		if(server_token==null){
			return false;
		}
		if(!client_token.equals(server_token)){
			return false;
		}
		return true;
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}
}

你可能感兴趣的:(java,web基础)