Cookie&Session
**Cookie:**客户端会话跟踪技术
- 用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含**多次**请求和响应。
**Session:**服务端会话跟踪技术
- 服务器用来识别浏览器的过程,这个过程就是**会话跟踪**
1. Cookie
**Cookie:**客户端会话技术,将数据保存到客户端,以后每次请求都携带Cookie数据进行访问。
1.1 Cookie的基本使用
对于Cookie的使用,我们更关注的应该是后台代码如何操作Cookie,对于Cookie的操作主要分两大类,分别是发送Cookie和获取Cookie,对于上面这两块内容,分别该如何实现呢?
发送Cookie
- 创建Cookie对象,并设置数据
Cookie cookie = new Cookie("key","value");
- 发送Cookie到客户端:使用response对象
response.addCookie(cookie);
获取Cookie
- 获取客户端携带的所有Cookie,使用request对象
Cookie[] cookies = request.getCookies();
- 遍历数组,获取每一个Cookie对象:for
- 使用Cookie对象方法获取数据
cookie.getName();
cookie.getValue();
1.2 代码实现
介绍完获取Cookie创建和获取后,做个案例实现
需求:在AServlet中存入数据,在BServlet中获取数据。
AServlet
@WebServlet("/AServlet")
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie cookie = new Cookie("name","zhangsan");
//设置存活时间 ,1周 7天
cookie.setMaxAge(60*60*24*7);
response.addCookie(cookie);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
BServlet
@WebServlet("/BServlet")
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
System.out.println("cookie = " + name+":"+value);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
总结:
在这节中,我们主要讲解了Cookie的基本使用,包含两部分内容
- 发送Cookie:
- 创建Cookie对象,并设置值:Cookie cookie = new Cookie("key","value");
- 发送Cookie到客户端使用的是Reponse对象:response.addCookie(cookie);
- 获取Cookie:
- 使用Request对象获取Cookie数组:Cookie[] cookies = request.getCookies();
- 遍历数组
- 获取数组中每个Cookie对象的值:cookie.getName()和cookie.getValue()
1.3 Cookie的原理分析
对于Cookie的实现原理是基于HTTP协议的,其中设计到HTTP协议中的两个请求头信息:
响应头:set-cookie
请求头: cookie

前面的案例中已经能够实现,AServlet给前端发送Cookie,BServlet从request中获取Cookie的功能
对于AServlet响应数据的时候,Tomcat服务器都是基于HTTP协议来响应数据
当Tomcat发现后端要返回的是一个Cookie对象之后,Tomcat就会在响应头中添加一行数据==
Set-Cookie:username=zs==浏览器获取到响应结果后,从响应头中就可以获取到
Set-Cookie对应值username=zs,并将数据存储在浏览器的内存中浏览器再次发送请求给BServlet的时候,浏览器会自动在请求头中添加==
Cookie: username=zs==发送给服务端BServletRequest对象会把请求头中cookie对应的值封装成一个个Cookie对象,最终形成一个数组
BServlet通过Request对象获取到Cookie[]后,就可以从中获取自己需要的数据
1.4 Cookie的使用细节
在这节我们主要讲解两个知识,第一个是Cookie的存活时间,第二个是Cookie如何存储中文。
1.4.1 Cookie的存活时间
默认情况下,Cookie存储在浏览器内存中,当浏览器关闭,内存释放,则Cookie被销毁。
如何将Cookie持久化存储?
Cookie其实已经为我们提供好了对应的API来完成这件事,这个API就是setMaxAge
- 设置Cookie存活时间
setMaxAge(int seconds)
参数值为:
1.正数:将Cookie写入浏览器所在电脑的硬盘,持久化存储。到时间自动删除
2.负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁
3.零:删除对应Cookie
Cookie cookie = new Cookie("name","zhangsan");
//设置存活时间 ,1周 7天
cookie.setMaxAge(60*60*24*7);
response.addCookie(cookie);
1.4.2 Cookie存储中文
tomcast8.0之前由于tomcat默认编码是ISO 8859-1 不支持cookie存储中文。tomcat8.0之后,支持存入中文
Tomcat7 Cookie不能直接存储中文,但是如果有这方面的需求,这个时候该如何解决呢?
这个时候,我们可以使用之前学过的一个知识点叫URL编码,所以如果需要存储中文,就需要进行转码,具体的实现思路为:
1.在AServlet中对中文进行URL编码,采用URLEncoder.encode(),将编码后的值存入Cookie中
2.在BServlet中获取Cookie中的值,获取的值为URL编码后的值
3.将获取的值在进行URL解码,采用URLDecoder.decode(),就可以获取到对应的中文值
存中文Cookie
String name = "张三";
//cookie存入中文,使用URLEncoder
name = URLEncoder.encode(name, "UTF-8");
// Cookie cookie = new Cookie("name","zhangsan");
Cookie cookie = new Cookie("name",name);
//设置存活时间 ,1周 7天
cookie.setMaxAge(60*60*24*7);
response.addCookie(cookie);
取中文Cookie
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
// System.out.println("cookie = " + name+":"+value);
if ("name".equals(name)) {
String decode = URLDecoder.decode(value, "UTF-8");
System.out.println("decode = " + decode);
}
}
2. Session
**Session:**是服务端技术,将数据保存到服务端,Session是基于Cookie实现的。
Session的工作流程

- 在服务端的AServlet获取一个Session对象,把数据存入其中
- 在服务端的BServlet获取到相同的Session对象,从中取出数据
- 就可以实现一次会话中多次请求之间的数据共享了
- 现在最大的问题是如何保证AServlet和BServlet使用的是同一个Session对象
2.1 Session的基本使用
具体的使用步骤为:
- 获取Session对象,使用的是request对象
HttpSession session = request.getSession();
Session对象提供的功能:
存储数据到 session 域中
void setAttribute(String name, Object o)根据 key,获取值
Object getAttribute(String name)根据 key,删除该键值对
void removeAttribute(String name)
2.2 代码实现
介绍完Session相关的API后,接下来通过一个案例来完成对Session的使用
需求:在一个Servlet中往Session中存入数据,在另一个Servlet中获取Session中存入的数据
创建名为SessionDemo1的Servlet类,获取Session对象、存储数据
@WebServlet("/SessionDemo1") public class SessionDemo1 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); session.setAttribute("username","zs"); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }创建名为SessionDemo2的Servlet类,获取Session对象、获取数据
@WebServlet("/SessionDemo2") public class SessionDemo2 extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); String username = (String) session.getAttribute("username"); System.out.println("username = " + username); // 销毁 session.invalidate(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } }
- **注意:**Session中可以存储的是一个Object类型的数据,也就是说Session中可以存储任意数据类型。
介绍完Session的基本使用之后,那么Session的底层到底是如何实现一次会话两次请求之间的数据共享呢?
2.3 Session的原理分析
Session是基于Cookie实现的

Session要想实现一次会话多次请求之间的数据共享,就必须要保证多次请求获取Session的对象是同一个。
那么它们是一个对象么?答案是肯定的,要验证这个结论也很简单,只需要在上面案例中的两个Servlet中分别打印下Session对象
那么最主要的问题就来了,Session是如何保证在一次会话中获取的Session对象是同一个呢?

SessionDemo1在第一次获取session对象的时候,session对象会有一个唯一的标识,假如是
id:10SessionDemo1在session中存入其他数据并处理完成所有业务后,需要通过Tomcat服务器响应结果给浏览器
Tomcat服务器发现业务处理中使用了session对象,就会把session的唯一标识
id:10当做一个cookie,添加Set-Cookie:JESSIONID=10到响应头中,并响应给浏览器浏览器接收到响应结果后,会把响应头中的coookie数据存储到浏览器的内存中
浏览器在同一会话中访问SessionDemo2的时候,会把cookie中的数据按照
cookie: JESSIONID=10的格式添加到请求头中并发送给服务器TomcatSessionDemo2获取到请求后,从请求头中就读取cookie中的JSESSIONID值为10,然后就会到服务器内存中寻找
id:10的session对象,如果找到了,就直接返回该对象,如果没有则新创建一个session对象关闭打开浏览器后,因为浏览器的cookie已被销毁,所以就没有JESSIONID的数据,服务端获取到的session就是一个全新的session对象
至此,Session是基于Cookie来实现的这就话,我们就解释完了。
2.4 Session的使用细节
这节我们会主要讲解两个知识,第一个是Session的钝化和活化,第二个是Session的销毁,首先来学习什么是Session的钝化和活化?
2.4.1 Session钝化与活化
**疑问:**服务器重启后,Session中的数据是否还在?
- 服务器端AServlet和BServlet共用的session对象应该是存储在服务器的内存中
- 服务器重新启动后,内存中的数据应该是已经被释放,对象也应该都销毁了, Tomcat使用钝化****和活化,保证对象的唯一性。
- 服务器是正常关闭和启动,session中的数据是可以被保存下来的。
**钝化:**在服务器正常关闭后,Tomcat会自动将Session数据写入硬盘的文件中
- 钝化的数据路径为:`项目目录\target\tomcat\work\Tomcat\localhost\项目名称\SESSIONS.ser`
**活化:**再次启动服务器后,从文件中加载数据到Session中
- 数据加载到Session中后,路径中的
SESSIONS.ser文件会被删除掉
2.4.2 Session销毁
session的销毁会有两种方式:
默认情况下,无操作,30分钟自动销毁
对于这个失效时间,是可以通过配置进行修改的
在项目的web.xml中配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!-- 配置session存活时间,100(单位分钟)--> <session-config> <session-timeout>100</session-timeout> </session-config> </web-app>如果没有配置,默认是30分钟,默认值是在Tomcat的web.xml配置文件中写死的
调用Session对象的invalidate()进行销毁
HttpSession session = request.getSession(); String username = (String) session.getAttribute("username"); System.out.println("username = " + username); // 销毁 session.invalidate();
3. Cookie和Session小结
- Cookie 和 Session 都是来完成一次会话内多次请求间==数据共享==的。
所需两个对象放在一块,就需要思考:
Cookie和Session的区别是什么?
Cookie和Session的应用场景分别是什么?
- 区别:
- 存储位置:Cookie 是将数据存储在客户端,Session 将数据存储在服务端
- 安全性:Cookie不安全,Session安全
- 数据大小:Cookie最大3KB,Session无大小限制
- 存储时间:Cookie可以通过setMaxAge()长期存储,Session默认30分钟
- 服务器性能:Cookie不占服务器资源,Session占用服务器资源
- 应用场景:
- 购物车:使用Cookie来存储
- 以登录用户的名称展示:使用Session来存储
- 记住我功能:使用Cookie来存储
- 验证码:使用session来存储
- 结论
- Cookie是用来保证用户在未登录情况下的身份识别
- Session是用来保存用户登录后的数据
介绍完Cookie和Session以后,具体用哪个还是需要根据具体的业务进行具体分析。
4. 用户登录注册案例
4.1 需求分析
需求说明:
完成用户登录功能,如果用户勾选**“记住用户”** ,则下次访问登录页面自动填充用户名密码
完成注册功能,并实现验证码功能

4.1.1 用户登录功能
需求:用户登录成功后,跳转到登陆并在页面上展示当前登录的用户名称,选中记住密码,重新进入登陆页,自动填充用户名和密码
前端通过表单发送请求和数据给Web层的LoginServlet
在LoginServlet中接收请求和数据[用户名和密码]
在LoginServlet模拟数据库中存在的用户名和密码,如果输入正确,并判断是否记录密码,则使用Cookie响应给浏览器,存储用户名密码
技术难点:
如何在JSP页面直接获取Cookie中的值呢?
在页面可以使用EL表达式,${cookie.==key==.value}
key:指的是存储在cookie中的键名称
在login.jsp用户名的表单输入框使用value值给表单元素添加默认值,value可以使用
${cookie.username.value}在login.jsp密码的表单输入框使用value值给表单元素添加默认值,value可以使用
${cookie.password.value}
代码实现
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆</title>
<link href="css/login.css" rel="stylesheet">
</head>
<body>
<div id="loginDiv" style="height: 350px">
<form action="/09CookieSession/LoginServlet" method="post" id="form" >
<h1 id="loginMsg">登陆</h1>
<div id="errorMsg">${login_msg}</div>
<p>用户名:<input id="username" name="username" type="text" value="${cookie.username.value}"></p>
<p>密 码:<input id="password" name="password" type="password" value="${cookie.pd.value}"></p>
<p>记住密码:<input id="remember" name="remember" value="1" type="checkbox"></p>
<div id="subDiv">
<input type="submit" class="button" value="登陆">
<input type="reset" class="button" value="重置">
<a href="/09CookieSession/register.jsp">没有账号?</a>
</div>
</form>
</div>
</body>
</html>
LoginServlet
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
//模拟从数据查询的数据
String name = "zhangsan";
String password = "123456";
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String pd = request.getParameter("password");
String remember = request.getParameter("remember");
if (name.equals(username) && password.equals(pd)) {
response.setContentType("text/html;charset = utf-8");
response.getWriter().write("<h1>" + username + "欢迎你,登陆成功</h1>");
//选中记住密码
if ("1".equals(remember)) {
//想客户端发送用户名密码,保存
Cookie cookie1 = new Cookie("username", username);
// 设置Cookie的存活时间,一周
cookie1.setMaxAge(60 * 60 * 24 * 7);
Cookie cookie2 = new Cookie("pd", pd);
cookie2.setMaxAge(60 * 60 * 24 * 7);
response.addCookie(cookie1);
response.addCookie(cookie2);
}
} else {
request.setAttribute("login_msg", "用户名或密码错误");
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
4.2 用户注册功能
需求:用户注册有2个功能
- 获取jsp页面输入的用户名密码,在Servlet模拟用户注册
- 在jsp注册页面,可以动态获取验证码
技术难点:
在register.jsp如何事实的获取验证码
- 前端发送请求给CheckCodeServlet
- CheckCodeServlet接收到请求后,生成验证码图片,将图片用Reponse对象的输出流写回到前端
思考:如何将图片写回到前端浏览器呢?
- Java中已经有工具类生成验证码图片,测试类中只是把图片生成到磁盘上
- 生成磁盘的过程中使用的是OutputStream流,如何把这个图片生成在页面呢?
- 前面在将Reponse对象的时候,它有一个方法可以获取其字节输出流,getOutputStream()
- 综上所述,我们可以把写往磁盘的流对象更好成Response的字节流,即可完成图片响应给前端
jsp请求服务器生成新的验证码,核心代码
<tr>
<td>验证码</td>
<td class="inputs">
<input name="checkCode" type="text" id="checkCode">
<img id="checkCodeImg" src="/brand-demo/checkCodeServlet">
<a href="#" id="changeImg" >看不清?</a>
</td>
</tr>
<script>
document.getElementById("changeImg").onclick = function () {
//路径后面添加时间戳的目的是避免浏览器进行缓存静态资源
document.getElementById("checkCodeImg").src = "/brand-demo/checkCodeServlet?"+new Date().getMilliseconds();
}
</script>
代码实现
register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>欢迎注册</title>
<link href="css/register.css" rel="stylesheet">
</head>
<body>
<div class="form-div">
<div class="reg-content">
<h1>欢迎注册</h1>
<span>已有帐号?</span> <a href="/09CookieSession/login.jsp">登录</a>
</div>
<form id="reg-form" action="/09CookieSession/RegisterServlet" method="post">
<table>
<tr>
<td>用户名</td>
<td class="inputs">
<input name="username" type="text" id="username">
<br>
<span id="username_err" class="err_msg" style="display: none">用户名不太受欢迎</span>
</td>
</tr>
<tr>
<td>密码</td>
<td class="inputs">
<input name="password" type="password" id="password">
<br>
<span id="password_err" class="err_msg" style="display: none">密码格式有误</span>
</td>
</tr>
<tr>
<td>验证码</td>
<td class="inputs">
<input name="checkCode" type="text" id="checkCode">
<%-- <img src="imgs/a.jpg">--%>
<img id="checkCodeImg" src="/09CookieSession/CheckCodeServlet">
<a href="#" id="changeImg">看不清?</a>
</td>
</tr>
</table>
<div class="buttons">
<input value="注 册" type="submit" id="reg_btn">
</div>
<br class="clear">
</form>
</div>
<script>
document.getElementById("changeImg").onclick = function () {
//路径后面添加时间戳的目的是避免浏览器进行缓存静态资源
document.getElementById("checkCodeImg").src = "/09CookieSession/CheckCodeServlet?"+new Date().getMilliseconds();
}
</script>
</body>
</html
RegisterServlet
@WebServlet("/RegisterServlet")
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//记得设置编码,不然转发的页面可能乱码
response.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
String password = request.getParameter("password");
String checkCode = request.getParameter("checkCode");
//模拟用户注册,
response.getWriter().write(username+"-"+password+"-"+checkCode);
//比对用户输入的验证码和服务器生成的验证码是否一致
HttpSession session = request.getSession();
Object checkCodeGen = session.getAttribute("checkCodeGen");
response.setContentType("text/html;charset = utf-8");
if (checkCodeGen.equals(checkCode)) {
request.setAttribute("register_msg","注册成功,请登录");
request.getRequestDispatcher("/login.jsp").forward(request,response);
}else {
request.setAttribute("register_msg","验证码错误");
request.getRequestDispatcher("/register.jsp").forward(request,response);
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
CheckCodeServlet:生成验证码的Servlet
/**
* 使用验证码工具类生成图片,发送给jsp页面
*/
@WebServlet("/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 生成验证码
ServletOutputStream os = response.getOutputStream();
String checkCode = CheckCodeUtil.outputVerifyImage(100, 50, os, 4);
// 将生成的验证码,存入Session
HttpSession session = request.getSession();
session.setAttribute("checkCodeGen",checkCode);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
