测试方向基础——会话管理浅析


持续更新中。。。

了解会话管理的概念和基本原理

  1. 为什么要进行会话管理?
    Web应用程序基于HTTP协议
    (1)HTTP基于请求/响应模式,所有请求都是相互独立的、无连续性的
    (2)HTTP是无连接的协议,限制每次连接只处理一个请求
    (3)HTTP是无状态的协议,协议对于事务处理没有记忆能力

  2. 会话管理的产生:
    (1)对于简单的页面浏览或信息获取,HTTP协议即可胜任(例如浏览、查看在线图书目录)
    (2)对于需要客户端和服务器端多次交互的网络应用,则必须记住客户端状态(例如网上的购物车、用户登录)

  3. 会话就是 一个客户端连续不断地和服务器端进行请求/响应的一系列交互

  4. 多次请求建立关联的方式称为会话管理或会话跟踪
    会话状态: 指服务器与浏览器在会话过程中产生的状态信息。
    (HTTPServlet有一个全局的context对象**(ServletContext context = getServletContext()、 context.setAttribute())**,可以把所有信息存起来,但像淘宝那么多用户,都存进去,太多了。所以不建议使用。)

  5. 会话的实现过程:
    在这里插入图片描述

使用Cookie、隐藏域、URL重写实现会话管理

  1. HTTP有两大部分:请求和响应
    (1)请求:包含请求头、请求行、请求体(仅限于post)
    (2)响应:响应头、响应行、响应体(响应正文)
  2. 使用Cookie
    所有HTTP消息,不管是请求还是响应均包含头信息。
    (1)当服务器返回响应给客户端时,Servlet容器把会话的信息添加到响应头信息中
    (2)客户端浏览器接收到响应后提取头信息,并将其存储在本地机中,以后发送请求时会自动将该信息带回服务器端
    浏览器存储在客户端机器上的头信息称作Cookie, 它以**属性名=属性值;…**方式组成文本信息。
  3. 写入在这里插入图片描述(1)创建Cookie对象:调用Cookie的构造方法,给出Cookie的名称和Cookie的值,二者都是字符串。上例中为Cookie c = new Cookie("userName", "a1234")(2)设置最大时效,如果要告诉浏览器将Cookie存储到磁盘上,而非仅保存在内存中,使用setMaxAge方法(参数为秒数)。c.setMaxAge(60*60*24*7)//一周(3)将Cookie放入到HTTP响应中。(如果没有这一步,不会有任何Cookie被送到浏览器) response.addCookie(c);
  4. 读取cookie:调用request,getCookies(得到Cookie对象组成的数组,)
    举个例子:(上述图片上的基本操作)
package cookie;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.smartcardio.TerminalFactorySpi;
import javax.xml.ws.Response;
@WebServlet("/c1")
public class CookieDemo1 extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=utf-8");
//1.创建Cookie对象
		Cookie cookie1 = new Cookie("username", "xhx&");
//2.设置最大时效
		cookie1.setMaxAge(60*60*24);
//3.添加到响应中
		resp.addCookie(cookie1);
		Cookie cookie2 = new Cookie("password", "123");
		cookie2.setMaxAge(60*60*24);
		resp.addCookie(cookie2);
		PrintWriter writer = resp.getWriter();
		writer.write("cookie, first!");
	}
}

package cookie;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
 * 获取cookie
 * */

@WebServlet("/c2")
public class CookieDemo2 extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		resp.setContentType("text/html;charset=utf-8");
		Cookie[] cookies = req.getCookies();
		PrintWriter out = resp.getWriter();
		if (cookies != null) {
			System.out.println(cookies.length);
			out.write("cookie的信息:" + "<br>");
			for (Cookie cookie : cookies) {
				out.write(cookie.getName() + ":" + cookie.getValue());
			}
		}
	}
}

效果如下:
在这里插入图片描述
下面展示一个Cookie当一个计数器的栗子。

package cookie;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.sun.jndi.url.iiopname.iiopnameURLContextFactory;

/*
 * 如果第一次访问,创建Cookie,访问次数设为1
 * 不是第一次,取出Cookie的值,次数+1
 * */
@WebServlet("/CookieCounter")
public class CookieCounter extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.设置响应编码
		resp.setContentType("text/html;charset=utf-8");
		PrintWriter out = resp.getWriter();
//2.获取请求
		Cookie[] cookies = req.getCookies();
		if (cookies == null) { // 第一次访问
			out.println("这是第一次访问");
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd/hh:mm");

			Cookie c1 = new Cookie("date", sdf.format(new Date()));
			c1.setMaxAge(60 * 60);
			resp.addCookie(c1);

			Cookie c2 = new Cookie("count", "1"); // value值只能是字符串!
			c2.setMaxAge(60 * 60);
			resp.addCookie(c2);
		} else {
			for (int i = 0; i < cookies.length; i++) {
				if (cookies[i].getName().equals("date")) {
					out.write("第一次访问的时间: " + cookies[i].getValue() + "<br>");
				} else { // 次数加一
					String counter = cookies[i].getValue();
					int count = Integer.parseInt(counter);
					count++;
					out.write("第" + count + "次访问" + "<br>");

					// 第一种方法:重写一个cookie代替
					Cookie new_c2 = new Cookie("count", String.valueOf(count));
					new_c2.setMaxAge(60 * 60);
					resp.addCookie(new_c2);

					// 第二种,setValue
					// cookies[i].setValue(String.valueOf(count));
					/* 注意只有setValue没有setName() */
					// resp.addCookie(cookies[i]);

				}
			}
		}
	}
}

效果:
在这里插入图片描述

【tips:可能出现(java.lang.NumberFormatException: For input string: “xhx&”)类似这种报错:解决办法:清空浏览器的cookie数据。(在设置中)】

  1. Cookie的优缺点:
    优点:
    (1)可配置到期规则,数据可持久保存
    (2)不需要服务器资源,数据保存在客户端
    (3)简单性,基于文本的Key-Value对
    缺点:
    (1)大小受到限制(总数:300; 站点:20; Cookie:4KB)
    (2)用户可禁用客户端接收Cookie的功能
    (3)潜在的安全风险。

  2. 使用隐藏的表单
    (1)思想 :通过使用隐藏域,由浏览器主动告知服务器多次请求间必要的信息,如:在线问卷作答。
    例如下图:
    在这里插入图片描述

(2)优缺点:
1)优点:Cookie被禁用或者根本不支持的情况下一九能够工作
2)缺点:关掉网页后会遗失先前的请求结果;所有的页面必须是表单提交之后的结果。

  1. 使用URL重写
    (1)思想:当服务器响应浏览器上一次请求时,将某些相关信息以超链接方式响应给浏览器,超链接中包括请求参数信息。
    例如:
    在这里插入图片描述
    举一个简单的例子:点击Servlet1中的链接,会跳转到Servlet2并且获得URL中的相关参数
package cookie;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/Servlet1")
public class Servlet1 extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=utf-8");
		//重新编码URL
		String path = response.encodeURL("/xueli02/Servlet2?name=xhx&passwd=123456");
		PrintWriter out = response.getWriter();
		out.write("<a href="+path+">click here</a>");
		out.flush();
	}

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

package cookie;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/Servlet2")
public class Servlet2 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		out.write(request.getParameter("name"));
		out.flush();
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
}

练习:尝试实现三天免登录的功能:(但仅仅有这些代码,还不足以实现这个功能,因此在此只做一个记录。若有朝一日我能把这个写出来,再来此补充。(如果有大佬指点,那就更好了。。呜呜呜,菜死了))
一:LoginServlet.java:
如果是用户名admin,密码是123456,
(1)并且勾选自动登录,创建Cookie,存储用户名,有效期为3天。
(2)未勾选,不创建Cookie,请求转发到UserServlet.java
如果用户名密码错误,重定向到login.html
二:UserServlet.java:
如果用户名为空,重定向到login.html
如果不为空,显示用户名

三:IndexServlet.java:
获取Cookie,如果包含user:admin,跳转到UserServlet.java
否则,重定向到login.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="login" method="post">
用户名:<input type="text" name="uname"><br>
密码:<input type="text" name="upwd"><br>
自动登录:<input type="checkbox" name="is_login" value="auto"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
package cookie;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		response.getWriter().append("Served at: ").append(request.getContextPath());
	}

	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String username=request.getParameter("uname");
		String password =request.getParameter("upwd");
		if(username.equals("admin")&&password.equals("123456")) {
			String flag = request.getParameter("is_login");
			if(flag.equals("auto")) {
				Cookie cookie = new Cookie("name", username);
				cookie.setMaxAge(60*60*24*3);
				response.addCookie(cookie);
			}
			request.setAttribute("username", username);
			request.getRequestDispatcher("user").forward(request, response);
		}else {
			response.sendRedirect("login.html");
		}
	}

}

package cookie;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/UserServlet")
public class UserServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		if(request.getAttribute("username")==null) {
			response.sendRedirect("login.html");
		}else {
			PrintWriter writer = response.getWriter();
			writer.write(""+request.getAttribute("username"));
		}
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

package cookie;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class IndexServlet
 */
@WebServlet("/IndexServlet")
public class IndexServlet extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Cookie[] cookies = request.getCookies();
		if(cookies!=null) {
			for(Cookie cookie:cookies) {
				String name=cookie.getName();
				String value=cookie.getValue();
				if(name.equals("name") && value.equals("admin")) {
					request.setAttribute("username", value);
					request.getRequestDispatcher("user").forward(request, response);
				}
			}
		}
		response.sendRedirect("login.html");
	}
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

Session会话管理的原理和技术实现

  1. 在Servlet中进行会话管理,可以使用HttpServletRequest的getSession()方法获取HttpSession对象(简称为Session),通过设置/获取服务器端Session对象的属性,来保留请求间的相关信息。如下图所示:
    在这里插入图片描述

  2. Servlet容器提供Session接口来代表服务器端和客户端的会话
    当一个WEB服务器为客户端开始一个会话时,创建一个Session对(含有特殊ID,称为Session ID,默认用Cookie存放在浏览器中。在Tomacat中,Cookie的名称为JSESSIONID)临时存储,浏览器关闭就失效了
    Session将数据存储在服务器的内存中,供以后来自同一个客户端的请求使用。
    第二次请求中(接下来的多次请求),请求头会附带JSESSIONID,服务器接收到请求后,调用相应的Servlet处理,同时跟据JSESSIONID,返回对应的Session对象。

    举一个例子:

package hebu.couse.xxx;

import java.io.IOException;

import javax.security.auth.message.callback.PrivateKeyCallback.Request;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@WebServlet("/SessionDemo1")
public class SessionDemo1 extends HttpServlet{
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		HttpSession session = req.getSession();
		System.out.println(session.getId());
		session.setAttribute("name", "xhx");
		resp.sendRedirect("SessionDemo2");
	}
}
package hebu.couse.xxx;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/SessionDemo2")
public class SessionDemo2 extends HttpServlet {
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		HttpSession session = request.getSession();
		System.out.println("2----"+session.getId());
		response.getWriter().write((String)session.getAttribute("name"));
	}
}

打印出来内容如下:
在这里插入图片描述
补充:如果session.getAttribute(“name”)不存在,返回null

举个例子:
当第一次输入用户名的时候,页面显示:这是第一次登录,及用户名。
后面访问,就直接显示用户名

package hebu.couse.xxx;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

@WebServlet("/Login")
public class LoginServlet extends HttpServlet {

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

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.设置请求编码方式
		request.setCharacterEncoding("utf-8");
//2.设置响应的编码方式:
		response.setContentType("text/html;charset=utf-8");
		PrintWriter writer = response.getWriter();
//3.获取请求信息
		String user_name = request.getParameter("uname");
//4.处理请求
		if(user_name.equals("yyy")) {
			HttpSession session = request.getSession();
			System.out.println(session.getId());
			String name=(String)session.getAttribute("user");
			if(name == null){//第一次访问为空
				writer.write("这是第一次访问");
				writer.write("<h1>"+user_name+"</h1>");
				session.setAttribute("user", user_name);
				//session.setMaxInactiveInterval(60*60);
				//在1小时之内,当前站点没有被访问,session对象就销毁了
				//session.invalidate();//强制session失效,多用于退出
			} else {
				writer.write("<h1>"+user_name+"</h1>");
			}
		}
//5.返回响应
	}
}

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="Login" method="post">
用户名:<input type="text" name="uname"><br>
密码:<input type="text" name="upwd"><br>

<input type="submit" value="登录">
</form>
</body>
</html>

上述例子,可以发现,每次打印出来的ID是一样的。
我们还可以session.invalidate();//强制session失效,多用于退出

使用时机:
登录,把用户的信息保存在session中,供这个用户的其他请求使用。
总结:
Session解决了一个用户的不同请求的数据共享问题,只要JSSESSIONID不失效(浏览器关闭)和Session对象不失效(超时)
当前用户的对于该站点的任意Servlet请求在处理时都能获取同一个 session的对象。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xuhx&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值