会话技术
概述
用户打开一个浏览器,点击多个超链接访问服务器的web资源,然后关闭浏览器,整个过程称为会话。每个用户与服务器交互都会产生各自的数据,保存这些数据需要使用会话技术。
为何不能用Request或者ServletContext:
会话技术分类
- Cookie(服务器保存到客户端的键值对)
- 客户端的技术
- 把每个用户的数据以cookie的形式保存到浏览器中;当用户使用浏览器再次访问服务器中web资源时,会带着各自cookie过去。这样web资源处理的就是各自的数据
- Session
- 服务器端的技术
- 服务器在运行时为每一个浏览器用户创建一个独享的session对象;用户访问服务器时,可以把各自数据放入各自的session中或者取用各自session中的数据
Cookie实现原理
Session实现原理
用Cookie记录上次访问时间
需求分析:
代码实现:(Cookie的简单使用)
HttpServletRequest - Cookies[] getCookies() -- 返回浏览器带回来的所有cookie
HttpServletResponse - void addCookie(Cookie cookie) -- 向浏览器回写cookie
Cookie - Cookie(String name, String value) -- 构造Cookie
package com.servlet.utils;
import javax.servlet.http.Cookie;
/**
* 查找指定名称Cookie的工具类
*/
public class CookieUtils {
public static Cookie findCookie(Cookie[] cookies, String name) {
if (cookies == null) {
return null;
} else {
for (Cookie cookie : cookies) {
if (name.equals(cookie.getName())) {
return cookie;
}
}
return null; // 没有指定名称的Cookie情况
}
}
}
package com.servlet.demo;
import com.servlet.utils.CookieUtils;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class VisitServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 用户访问Servlet,第一次访问,显示欢迎,记录当前访问时间,存入cookie,回写到浏览器
* 非第一次访问,从cookie中获得上次的时间,显示到页面;再记录当前访问时间,存入cookie,回写到浏览器
*/
// 判断是否第一次访问
Cookie[] cookies = req.getCookies();
Cookie lastVisit = CookieUtils.findCookie(cookies, "lastVisit");
if (lastVisit == null) {
// 第一次访问
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("<h3>Welcome</h3>");
} else {
// 非第一次访问,获得上一次访问时间并显示
String value = lastVisit.getValue();
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().println("<h3>您上次访问的时间是:" + value + "</h3>");
}
// 记录当前的系统时间,存入cookie,回写浏览器
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
Cookie cookie = new Cookie("lastVisit", sdf.format(date));
resp.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
用Session保存用户登录信息
login.jsp
<%--登录表单,并显示错误信息--%>
<%--登陆表单包括用户名和密码,表单绝大部分指向LoginServlet--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>请登录!</h2>
<%
String uname = "";
// 读取cookie:uname
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("uname".equals(cookie.getName())) {
uname = cookie.getValue();
}
}
}
%>
<%
String message = "";
String msg = (String) request.getAttribute("message");
if (msg != null) {
message = msg;
}
%>
<font color="red"><%= message %>
</font>
<form action="/web_test3_war_exploded/LoginServlet" method="post">
用户名:<input type="text" name="username" value="<%=uname%>"> <br/>
密 码:<input type="text" name="password"> <br/>
<input type="submit" value="点击登录">
</form>
</body>
</html>
LoginServlet.java
/**
* 用来获取表单数据并且校验用户名和密码是否正确,错误时可以显示错误信息
* 正确时,保存用户信息到 session 中,重定向到 succ1.jsp
* 错误,保存错误信息到 request 域,转发回到 login.jsp
* 判断用户名与密码是否正确可自己设定
*/
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
String username = req.getParameter("username");
String password = req.getParameter("password");
// 校验
if ("root".equals(username) && "root123".equals(password)) {
// 用户名保存到cookie中
Cookie cookie = new Cookie("uname", username);
cookie.setMaxAge(60 * 60 * 24);
resp.addCookie(cookie);
// 保存用户信息到 session 中
HttpSession session = req.getSession();
session.setAttribute("username", username);
session.setAttribute("password", password);
// 重定向到 succ1.jsp
resp.sendRedirect("/web_test3_war_exploded/session/succ1.jsp");
} else { //登录失败
// 保存错误信息到 request 域
req.setAttribute("message", "您的用户名或密码错误");
// 转发回到 login.jsp
req.getRequestDispatcher("/session/login.jsp").forward(req, resp);
}
}
}
succ1.jsp
<%--
成功页面1 succ1.jsp (登录后的用户可以访问)
从session中获取用户信息,
如果存在,说明已经登录显示用户名;
如果不存在,向 request 域保存错误信息,转发到login.jsp
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h2>succ1.jsp</h2>
<%
String username = (String) session.getAttribute("username");
if (username == null) {
// 向 request 域保存错误信息,转发到login.jsp
request.setAttribute("message", "您没有登录成功,请重新登录!");
request.getRequestDispatcher("/session/login.jsp").forward(request, response);
return;
}
%>
欢迎<%= username %>登录本网站!
</body>
</html>
Cookie
Cookie的分类
- 默认级别的Cookie
- 指的是没有设置有效时间的Cookie,存在于浏览器的内存中
- 默认情况下只要关闭了浏览器,Cookie就会被销毁
- 持久级别的Cookie
- 指的是有有效时间的Cookie,持久化保存到硬盘上
- 再次打开浏览器时,会加载硬盘上的文件,数据就一直存在
常用API
- 构造方法:
- Cookie(String name, String value)
- 成员方法:
- String getName() 获得Cookie的名称
- String getValue() 获得Cookie的值
- void setDomain(String pattern) 设置Cookie的有效域名
- void setPath(String uri) 设置Cookie的有效路径
- void setMaxAge(int expiry) 设置Cookie的有效时长(单位是秒。设置为0就是删除)
一些使用细节
- 一个Cookie只能标识一种信息,至少含有一个标识该信息的名称和值
- 一个web网站可以给一个浏览器发送多个Cookie,一个web浏览器可以存储多个web站点的Cookie
- 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4k(老版的浏览器)。新版一般扩容了
- 如果创建了一个Cookie并发送到了浏览器中,默认情况下是一个会话级别的Cookie。用户退出浏览器,就被删除了。
- 如果希望将Cookie存到硬盘上,那么需要设置有效时长
- 需要手动删除持久性Cookie,可以将Cookie的有效时长设置为0。需要注意删除的时候,path必须一致,否则无法删除
Session
- Session也是一次会话,Cookie是将数据保存到浏览器中,Session是将数据保存到服务器端。
- 注意:一个浏览器会独占一个Session对象。
- 在需要保存用户数据时,写入到Session对象中,使用浏览器访问其他程序时,可以从Session中取出该用户的数据。
- 特点:(1)没有个数和大小的限制;(2)数据保存在服务器上,相对安全;
Session保存用户数据
Session对象由服务器创建,调用request对象的getSession()方法得到Session对象
Session实现原理
基于Cookie的,基于Cookie回写了Session的ID
Session作为域对象存取数据
HttpSession API:
- void setAttribute(String name, Object value) 往Session中存入数据
- Object getAttribute(String name) 从Session域中获取数据
- void removeAttribute(String name) 从Session域中移除数据
Session作为域对象的作用范围:
一次会话的范围;指的是用户打开浏览器,点击多个超链接访问服务器资源,最后关闭浏览器的过程
Servlet数据访问范围总结
| Servlet域对象 | 创建时机 | 销毁时机 | 存数据 | 取数据 | 作用范围 |
|---|---|---|---|---|---|
| 请求范围(ServletRequest) | 当用户向服务器发送一次请求,服务器就会创建一个request对象 | 当服务器对这次请求做出了响应,服务器就会销毁这个request对象 | 使用void setAttribute(String name, Object value)方法 | 使用Object getAttribute(String name)方法 | 一次请求(转发就是一次请求) |
| 会话范围(HttpSession) | 服务器端第一次去调用getSession()方法的时候,就会创建一个Session对象 | 三种情况:(1)Session过期(默认时间是30分钟)(2)非正常关闭服务器(正常关闭时Session会被序列化)(3)手动调用Session中的invalidate()方法 | 使用void setAttribute(String name, Object value)方法 | 使用Object getAttribute(String name)方法 | 一次会话(多次请求) |
| 应用范围(ServletContext)-全局范围 | 服务器启动的时候,为每个web项目创建一个单独的ServletContext对象 | 服务器关闭的时候,或web项目从服务器移除的时候 | 使用void setAttribute(String name, Object value)方法 | 使用Object getAttribute(String name)方法 | - - - 整个应用的范围 |