在 HTTP 无状态协议的背景下,Cookie 是客户端会话跟踪技术的核心实现,与服务器端的 Session 协同,共同解决用户状态保持问题。Cookie 是服务器发送给客户端浏览器的 "小数据块",存储在客户端,下次请求时自动携带,常用于记住用户名、自动登录、跟踪用户偏好等场景。
一、Cookie
1 概念
Cookie 是 HTTP 协议的一部分,指服务器在响应客户端请求时,通过响应头发送给客户端浏览器的小型文本数据(键值对形式)。客户端浏览器会将 Cookie 保存起来,当再次访问该服务器时,会将 Cookie 通过请求头携带回服务器,从而实现 "跟踪客户端状态" 的功能。
- 存储位置:客户端浏览器(内存或硬盘,取决于生命周期设置)
- 数据格式 :键值对(
name=value),支持字符串类型(不能直接存储中文,需编码) - 限制 (文档规范):
- 单个 Cookie 最大 4KB
- 浏览器最多保存 300 个 Cookie
- 不同浏览器之间不共享 Cookie(如 Chrome 的 Cookie 无法被 Firefox 读取)
2. 作用
- 记住用户身份(如登录后下次访问无需重复输入用户名)
- 实现自动登录(如 "7 天内自动登录" 功能)
- 跟踪用户行为(如电商网站记录用户浏览过的商品)
- 存储用户偏好(如网站主题、语言设置)
3 工作流程
- 客户端首次请求服务器,服务器创建 Cookie 对象,通过响应头(
Set-Cookie)发送给客户端 - 客户端浏览器接收 Cookie 后,按规则保存(内存或硬盘)
- 客户端再次访问该服务器时,将 Cookie 通过请求头(
Cookie)携带回服务器 - 服务器读取 Cookie 中的数据,识别客户端身份或状态,进行针对性处理
二、Cookie API 与基础用法
1. 创建 Cookie 并发送到客户端
Cookie 的创建和发送是服务器端的响应行为,通过HttpServletResponse的addCookie()方法实现。
1 API
Cookie(String name, String value):构造方法,创建 Cookie 对象(name 为键,value 为值)response.addCookie(Cookie cookie):将 Cookie 添加到响应中,发送给客户端(可多次调用添加多个 Cookie)
2 基础示例(CookieDemo1.java)
包名:cn.tx.servlet,创建 Cookie 并发送到客户端:
java
package cn.tx.servlet;
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.io.PrintWriter;
public class CookieDemo1 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 1. 创建Cookie对象(键名:username,值:txjava)
Cookie cookie = new Cookie("username", "txjava");
// 2. (可选)设置Cookie生命周期(默认仅存于浏览器内存)
// cookie.setMaxAge(60 * 60); // 1小时(秒为单位)
// 3. 将Cookie发送到客户端
response.addCookie(cookie);
// 响应提示
out.write("<h3>Cookie已发送到客户端!</h3>");
out.write("可通过浏览器开发者工具查看Cookie(F12→Application→Cookies)");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
3 配置 web.xml
XML
<!-- 注册CookieDemo1 -->
<servlet>
<servlet-name>CookieDemo1</servlet-name>
<servlet-class>cn.tx.servlet.CookieDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo1</servlet-name>
<url-pattern>/cookie/send</url-pattern>
</servlet-mapping>
4 验证效果
- 访问地址:
http://localhost:8080/javaweb0315/cookie/send - 打开浏览器 F12→Application→Cookies→
http://localhost:8080,可看到username=txjava的 Cookie - 查看响应头:F12→网络→当前请求→响应头,存在
Set-Cookie: username=txjava; Path=/javaweb0315(Path 为 Cookie 的默认路径)。
2. 服务器端读取客户端携带的 Cookie
客户端再次访问服务器时,会自动携带 Cookie,服务器通过HttpServletRequest的getCookies()方法读取,文档要求精通。
1 API
Cookie[] request.getCookies():获取客户端携带的所有 Cookie(无 Cookie 时返回null,需判空)String Cookie.getName():获取 Cookie 的键名String Cookie.getValue():获取 Cookie 的值
2 读取示例(CookieDemo2.java)
java
package cn.tx.servlet;
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.io.PrintWriter;
public class CookieDemo2 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("<h3>服务器读取客户端Cookie:</h3>");
// 1. 获取客户端携带的所有Cookie(需判空,无Cookie时返回null)
Cookie[] cookies = request.getCookies();
if (cookies != null && cookies.length > 0) {
// 2. 遍历Cookie数组,获取键名和值
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
out.write("Cookie名:" + name + " → 值:" + value + "<br/>");
System.out.println("Cookie名:" + name + " → 值:" + value); // 控制台同步打印
}
} else {
out.write("客户端未携带任何Cookie!");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
3 配置 web.xml
XML
<!-- 注册CookieDemo2 -->
<servlet>
<servlet-name>CookieDemo2</servlet-name>
<servlet-class>cn.tx.servlet.CookieDemo2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo2</servlet-name>
<url-pattern>/cookie/read</url-pattern>
</servlet-mapping>
4 验证效果
- 先访问
/cookie/send发送 Cookie - 再访问
/cookie/read读取 Cookie - 页面和控制台均会显示
Cookie名:username → 值:txjava,证明读取成功
3. Cookie 生命周期(文档重点,要求熟练)
Cookie 的生命周期指 Cookie 在客户端的存活时间,默认情况下,Cookie 仅存于浏览器内存中,关闭浏览器后即失效。可通过setMaxAge(int expiry)方法自定义生命周期(单位:秒)。
1 生命周期的 3 种设置
| 设置方式 | 代码示例 | 效果 | 存储位置 |
|---|---|---|---|
| 会话级(默认) | cookie.setMaxAge(-1)(负数均可) |
关闭浏览器后 Cookie 失效 | 浏览器内存 |
| 持久化(自定义时长) | cookie.setMaxAge(60 * 60 * 24) |
Cookie 存活 24 小时,即使关闭浏览器、重启电脑也有效 | 客户端硬盘 |
| 立即失效 (删除 Cookie) | cookie.setMaxAge(0) |
立即删除客户端已存在的同名 Cookie | 无(内存 + 硬盘均删除) |
2 生命周期示例(CookieDemo3.java)
java
package cn.tx.servlet;
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.io.PrintWriter;
public class CookieDemo3 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 1. 创建Cookie(键名需与要操作的Cookie一致)
Cookie cookie = new Cookie("username", "txjava");
// 2. 设置生命周期(三选一测试)
// 方式1:会话级(默认,关闭浏览器失效)
// cookie.setMaxAge(-1);
// 方式2:持久化1小时(60秒*60=3600秒)
cookie.setMaxAge(3600);
// 方式3:立即删除Cookie(需确保键名和路径与原Cookie一致)
// cookie.setMaxAge(0);
// cookie.setPath("/javaweb0315"); // 路径需与原Cookie一致
// 3. 发送Cookie(持久化/删除均需重新发送)
response.addCookie(cookie);
out.write("<h3>Cookie生命周期设置成功!</h3>");
out.write("1小时内关闭浏览器,再次访问/cookie/read仍能读取到该Cookie");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
3 配置 web.xml
XML
<!-- 注册CookieDemo3 -->
<servlet>
<servlet-name>CookieDemo3</servlet-name>
<servlet-class>cn.tx.servlet.CookieDemo3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo3</servlet-name>
<url-pattern>/cookie/lifecycle</url-pattern>
</servlet-mapping>
4 验证效果
- 访问
/cookie/lifecycle设置 Cookie 生命周期为 1 小时 - 关闭浏览器,重新打开,访问
/cookie/read,仍能读取到username=txjava的 Cookie - 1 小时后再次访问,Cookie 失效,无法读取(或调用方式 3 立即删除,访问后无法读取)
4. Cookie 的路径
Cookie 的path属性用于控制 "客户端在哪些请求路径下会携带该 Cookie",并非指 Cookie 在客户端的存储路径。
1 路径的默认规则
若不手动设置path,Cookie 的默认路径为 "当前请求路径的上一级":
- 例如:请求路径为
/javaweb0315/cookie/send,默认路径为/javaweb0315 - 例如:请求路径为
/javaweb0315/cookie/path/son,默认路径为/javaweb0315/cookie/path
2 路径的作用
客户端请求的路径包含 Cookie 的path时,才会携带该 Cookie:
- 示例 1:Cookie 路径为
/javaweb0315,请求/javaweb0315/xxx(如/javaweb0315/user)会携带 Cookie - 示例 2:Cookie 路径为
/javaweb0315/cookie,请求/javaweb0315/cookie/read会携带,请求/javaweb0315/user不会携带
3 手动设置路径(API)
cookie.setPath(String path):设置 Cookie 的路径,推荐设置为项目根路径(/项目名),确保整个项目都能访问该 Cookie。
4 路径示例(CookieDemo4.java)
java
package cn.tx.servlet;
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.io.PrintWriter;
public class CookieDemo4 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 1. 创建Cookie
Cookie cookie = new Cookie("pathCookie", "pathValue");
// 2. 手动设置路径为项目根路径(确保整个项目都能访问该Cookie)
String contextPath = request.getContextPath(); // /javaweb0315
cookie.setPath(contextPath);
// 3. 持久化1小时
cookie.setMaxAge(3600);
// 4. 发送Cookie
response.addCookie(cookie);
out.write("<h3>设置路径为" + contextPath + "的Cookie成功!</h3>");
out.write("访问项目内任意路径(如/user、/cookie/read)均可携带该Cookie");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
5 配置 web.xml
XML
<!-- 注册CookieDemo4 -->
<servlet>
<servlet-name>CookieDemo4</servlet-name>
<servlet-class>cn.tx.servlet.CookieDemo4</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo4</servlet-name>
<url-pattern>/cookie/path</url-pattern>
</servlet-mapping>
5. Cookie 保存中文
Cookie 的name和value不能直接存储中文(会抛出异常),需通过URLEncoder编码,获取时通过URLDecoder解码。
1 编码与解码示例(CookieDemo5.java)
java
package cn.tx.servlet;
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.io.PrintWriter;
import java.net.URLDecoder;
import java.net.URLEncoder;
public class CookieDemo5 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 1. 存储中文:URLEncoder编码
String chineseName = "张三";
String encodedName = URLEncoder.encode(chineseName, "UTF-8"); // 编码为%E5%BC%A0%E4%B8%89
Cookie cookie = new Cookie("chineseUser", encodedName);
cookie.setPath(request.getContextPath());
cookie.setMaxAge(3600);
response.addCookie(cookie);
// 2. 读取中文:URLDecoder解码
out.write("<h3>中文Cookie已存储,解码后:</h3>");
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if ("chineseUser".equals(c.getName())) {
String decodedValue = URLDecoder.decode(c.getValue(), "UTF-8"); // 解码为张三
out.write("中文用户名:" + decodedValue + "<br/>");
}
}
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
2 配置 web.xml
XML
<!-- 注册CookieDemo5 -->
<servlet>
<servlet-name>CookieDemo5</servlet-name>
<servlet-class>cn.tx.servlet.CookieDemo5</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CookieDemo5</servlet-name>
<url-pattern>/cookie/chinese</url-pattern>
</servlet-mapping>
三、Cookie 实战:实现自动登录
利用 Cookie 的持久化特性,实现 "一天内自动登录" 功能:用户登录时勾选自动登录,服务器将用户名密码(加密后)存入 Cookie,下次访问时自动验证登录。
1. 实现思路
- 登录页面提供 "自动登录" 复选框
- 登录成功且勾选自动登录时,将用户名密码加密后存入 Cookie(持久化 24 小时)
- 用户下次访问时,服务器读取 Cookie,解密后验证身份,验证通过则自动登录
2. 编写登录页面(auto_login.jsp)
XML
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>自动登录测试</title>
</head>
<body>
<h3>用户登录(支持自动登录)</h3>
<form action="${pageContext.request.contextPath}/autoLogin" method="post">
用户名:<input type="text" name="username" required><br><br>
密码:<input type="password" name="password" required><br><br>
<input type="checkbox" name="autoLogin" value="1"> 一天内自动登录<br><br>
<input type="submit" value="登录">
</form>
</body>
</html>
3. 编写自动登录 Servlet(AutoLoginServlet.java)
使用 BASE64 加密用户名密码(文档示例),避免明文存储:
java
package cn.tx.servlet;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
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.io.PrintWriter;
public class AutoLoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 解决中文乱码
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 1. 获取表单参数或Cookie中的登录信息
String username = request.getParameter("username");
String password = request.getParameter("password");
String autoLogin = request.getParameter("autoLogin");
// 2. 无表单参数时,尝试从Cookie读取自动登录信息
if (username == null && password == null) {
String cookieValue = null;
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if ("autoLoginCookie".equals(cookie.getName())) {
cookieValue = cookie.getValue();
break;
}
}
}
// 无Cookie信息:未登录,跳转到登录页
if (cookieValue == null) {
out.write("<h3>您尚未登录,请先登录!</h3>");
out.write("<a href='" + request.getContextPath() + "/auto_login.jsp'>前往登录</a>");
return;
}
// 有Cookie信息:解码并验证
BASE64Decoder decoder = new BASE64Decoder();
String decodedValue = new String(decoder.decodeBuffer(cookieValue), "UTF-8");
String[] info = decodedValue.split(":"); // 格式:前缀:用户名:密码:有效期
username = info[1];
password = info[2];
}
// 3. 模拟登录验证(实际项目查询数据库)
if ("admin".equals(username) && "123456".equals(password)) {
// 登录成功:判断是否勾选自动登录
if ("1".equals(autoLogin)) {
// 加密用户名密码(BASE64编码)
String value = "txjava:" + username + ":" + password + ":" + (24 * 3600);
BASE64Encoder encoder = new BASE64Encoder();
String encodedValue = encoder.encode(value.getBytes("UTF-8"));
// 创建Cookie,持久化24小时
Cookie cookie = new Cookie("autoLoginCookie", encodedValue);
cookie.setPath(request.getContextPath());
cookie.setMaxAge(24 * 3600); // 1天有效期
response.addCookie(cookie);
}
// 响应登录成功
out.write("<h3>登录成功!欢迎您," + username + "!</h3>");
out.write("<a href='" + request.getContextPath() + "/logoutAutoLogin'>退出登录</a>");
} else {
// 登录失败
out.write("<h3>用户名或密码错误!</h3>");
out.write("<a href='" + request.getContextPath() + "/auto_login.jsp'>返回登录</a>");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
4. 编写退出登录 Servlet(LogoutAutoLoginServlet.java)
退出登录时删除自动登录 Cookie:
java
package cn.tx.servlet;
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.io.PrintWriter;
public class LogoutAutoLoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 1. 创建同名Cookie,设置maxAge=0删除
Cookie cookie = new Cookie("autoLoginCookie", "");
cookie.setPath(request.getContextPath());
cookie.setMaxAge(0); // 立即删除
response.addCookie(cookie);
// 2. 响应退出结果
out.write("<h3>退出登录成功!自动登录已取消</h3>");
out.write("<a href='" + request.getContextPath() + "/auto_login.jsp'>重新登录</a>");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
5. 配置 web.xml
XML
<!-- 注册自动登录Servlet -->
<servlet>
<servlet-name>AutoLoginServlet</servlet-name>
<servlet-class>cn.tx.servlet.AutoLoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AutoLoginServlet</servlet-name>
<url-pattern>/autoLogin</url-pattern>
</servlet-mapping>
<!-- 注册退出登录Servlet -->
<servlet>
<servlet-name>LogoutAutoLoginServlet</servlet-name>
<servlet-class>cn.tx.servlet.LogoutAutoLoginServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LogoutAutoLoginServlet</servlet-name>
<url-pattern>/logoutAutoLogin</url-pattern>
</servlet-mapping>
6. 运行验证
- 访问登录页:
http://localhost:8080/javaweb0315/auto_login.jsp - 输入用户名(admin)、密码(123456),勾选 "自动登录",点击登录
- 关闭浏览器,重新打开,直接访问
http://localhost:8080/javaweb0315/autoLogin,无需输入账号密码,自动登录成功 - 点击 "退出登录",再次访问
/autoLogin,提示 "尚未登录"(Cookie 已删除)
四、要点总结
- API :创建(
new Cookie(name, value))、发送(response.addCookie())、读取(request.getCookies())、生命周期(setMaxAge())、路径(setPath()) - 中文处理 :需通过
URLEncoder编码、URLDecoder解码,不能直接存储中文 - 生命周期 :默认会话级(内存),
setMaxAge(正数)持久化(硬盘),setMaxAge(0)删除 - 路径作用:控制客户端在哪些请求路径下携带 Cookie,推荐设置为项目根路径
- 场景示例:自动登录、记住用户名、跟踪用户偏好
(1)Cookie 的工作原理是什么?
- 服务器创建 Cookie 对象,通过响应头
Set-Cookie发送给客户端 - 客户端浏览器保存 Cookie(内存或硬盘)
- 客户端再次访问服务器时,通过请求头
Cookie携带 Cookie 回服务器 - 服务器读取 Cookie 数据,识别客户端状态,实现会话跟踪
(2)Cookie 的生命周期如何控制?
- 默认:
setMaxAge(-1),仅存于浏览器内存,关闭浏览器失效 - 持久化:
setMaxAge(秒数),存于硬盘,到期或手动删除前有效 - 删除:
setMaxAge(0),立即删除客户端同名 Cookie(需保证路径一致)
(3)Cookie 和 Session 的区别是什么?
| 对比维度 | Cookie | Session |
|---|---|---|
| 存储位置 | 客户端(浏览器内存 / 硬盘) | 服务器端(内存 / 文件 / 数据库) |
| 数据安全性 | 低(明文存储,可篡改) | 高(数据不暴露给客户端) |
| 数据大小限制 | 单个 4KB,最多 300 个 | 无明确限制(受服务器资源影响) |
| 生命周期 | 可设置(会话级 / 持久化) | 会话级(超时 / 注销失效) |
| 依赖关系 | 独立存在,无需依赖其他组件 | 依赖 Cookie 传递 SessionID(Cookie 禁用则失效) |
| 场景 | 自动登录、记住用户名 | 登录状态保持、跨请求数据共享 |
(4)Cookie 如何保存中文?
Cookie 不能直接存储中文,需通过URLEncoder.encode(String value, "UTF-8")编码后存储,读取时通过URLDecoder.decode(String value, "UTF-8")解码。
(5)Cookie 禁用后,Session 还能使用吗?
默认不能,因为 Session 依赖 Cookie 传递 SessionID。可通过URL 重写 规避(在 URL 后拼接;jsessionid=SessionID),但存在安全风险(SessionID 暴露),不推荐使用。
五、所有地址汇总
- 发送 Cookie:
http://localhost:8080/javaweb0315/cookie/send - 读取 Cookie:
http://localhost:8080/javaweb0315/cookie/read - 生命周期测试:
http://localhost:8080/javaweb0315/cookie/lifecycle - 路径测试:
http://localhost:8080/javaweb0315/cookie/path - 中文 Cookie 测试:
http://localhost:8080/javaweb0315/cookie/chinese - 自动登录页面:
http://localhost:8080/javaweb0315/auto_login.jsp - 自动登录接口:
http://localhost:8080/javaweb0315/autoLogin - 退出自动登录:
http://localhost:8080/javaweb0315/logoutAutoLogin
结尾
Cookie 是 JavaWeb 客户端会话跟踪的技术,虽然数据存储在客户端存在安全风险(如 Cookie 劫持、篡改),但通过加密(如 BASE64、MD5)和合理的生命周期设置,可在一定程度上规避风险。
如果觉得本文对你有帮助,欢迎点赞 + 收藏 + 关注~