持续更新中。。。
了解会话管理的概念和基本原理
-
为什么要进行会话管理? Web应用程序基于HTTP协议 (1)HTTP基于请求/响应 模式,所有请求都是相互独立的、无连续性的 (2)HTTP是无连接 的协议,限制每次连接只处理一个请求 (3)HTTP是无状态 的协议,协议对于事务处理没有记忆能力 -
会话管理的产生: (1)对于简单的页面浏览或信息获取,HTTP协议即可胜任(例如浏览、查看在线图书目录) (2)对于需要客户端和服务器端多次交互的网络应用,则必须记住客户端状态(例如网上的购物车、用户登录) -
会话 就是 一个客户端连续不断地和服务器端进行请求/响应的一系列交互 -
多次请求建立关联的方式称为会话管理或会话跟踪 会话状态: 指服务器与浏览器在会话过程中产生的状态信息。 (HTTPServlet有一个全局的context对象**(ServletContext context = getServletContext()、 context.setAttribute())**,可以把所有信息存起来,但像淘宝那么多用户,都存进去,太多了。所以不建议使用。) -
会话的实现过程:
使用Cookie、隐藏域、URL重写实现会话管理
- HTTP有两大部分:请求和响应
(1)请求:包含请求头、请求行、请求体(仅限于post) (2)响应:响应头、响应行、响应体(响应正文) - 使用Cookie
所有HTTP消息,不管是请求还是响应均包含头信息。 (1)当服务器返回响应给客户端时,Servlet容器把会话的信息添加到响应头信息中 (2)客户端浏览器接收到响应后提取头信息,并将其存储在本地机中,以后发送请求时会自动将该信息带回服务器端 浏览器存储在客户端机器上的头信息 称作Cookie, 它以**属性名=属性值;…**方式组成文本信息。 - 写入(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); - 读取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");
Cookie cookie1 = new Cookie("username", "xhx&");
cookie1.setMaxAge(60*60*24);
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;
@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;
@WebServlet("/CookieCounter")
public class CookieCounter extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
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");
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 new_c2 = new Cookie("count", String.valueOf(count));
new_c2.setMaxAge(60 * 60);
resp.addCookie(new_c2);
}
}
}
}
}
效果:
【tips:可能出现(java.lang.NumberFormatException: For input string: “xhx&”)类似这种报错:解决办法:清空浏览器的cookie数据。(在设置中)】
-
Cookie的优缺点: 优点: (1)可配置到期规则,数据可持久保存 (2)不需要服务器资源,数据保存在客户端 (3)简单性,基于文本的Key-Value对 缺点: (1)大小受到限制(总数:300; 站点:20; Cookie:4KB) (2)用户可禁用客户端接收Cookie的功能 (3)潜在的安全风险。 -
使用隐藏的表单 (1)思想 :通过使用隐藏域,由浏览器主动告知服务器多次请求间必要的信息,如:在线问卷作答。 例如下图:
(2)优缺点: 1)优点:Cookie被禁用或者根本不支持的情况下一九能够工作 2)缺点:关掉网页后会遗失先前的请求结果;所有的页面必须是表单提交之后的结果。
使用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");
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 {
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 {
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 {
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;
@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 {
doGet(request, response);
}
}
Session会话管理的原理和技术实现
-
在Servlet中进行会话管理,可以使用HttpServletRequest的getSession()方法 获取HttpSession对象(简称为Session),通过设置/获取服务器端Session对象的属性,来保留请求间的相关信息。如下图所示: -
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 {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
String user_name = request.getParameter("uname");
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);
} else {
writer.write("<h1>"+user_name+"</h1>");
}
}
}
}
<!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的对象。
|