在Web开发中,Cookie是一个既熟悉又神秘的存在。它像一条看不见的纽带,连接着用户与服务器的每一次交互。尽管HTTP协议本身是"无状态"的,但正是通过Cookie,我们才能实现登录状态保持、购物车记录等功能。
本文将结合代码案例与知识库内容,深入浅出地讲解Cookie的原理、使用场景和实现方式。
一、Cookie是什么?
Cookie 是浏览器存储在用户设备上的小型文本数据 ,它用于记录用户会话、偏好等信息。它的核心作用是:让服务器识别用户身份。
1.1 为什么需要Cookie?
HTTP协议是无状态的。这意味着每次请求都是独立的,服务器不会记住用户之前的行为。例如:
- 用户登录后,服务器无法知道后续请求是否来自同一个用户。
- 就像你登录了一个电商网站(如淘宝),假如 HTTP是无状态的,那么你每次刷新页面或跳转到其他页面时,系统都会提示"请重新登录",这是因为服务器无法记住你之前的登录状态。
- 而通过Cookie,服务器在用户登录成功后设置一个标识。浏览器会将这个标识保存下来,并在后续请求中自动附加到请求头中,服务器通过解析这个标识就能知道用户已登录。
浏览器在每次HTTP请求中,自动携带Cookie信息,服务器通过解析这些信息识别用户身份。虽然HTTP协议本身无状态,但通过Cookie的"附加行为",实现了有状态的交互。
二、Cookie的工作原理
2.1 Cookie的生命周期
1. 服务器设置
当用户首次访问某个网站时,服务器可以通过HTTP响应头中的Set-Cookie
字段向浏览器发送Cookie,浏览器会根据规则存储这些数据。
示例:
javascript
res.writeHead(200, {
'Set-Cookie': "username=admin; Path=/; HttpOnly; Secure", // 设置Cookie
'Content-Type': 'application/json'
});
res.end(JSON.stringify({ success: true, msg: '登录成功' }));
以上代码会在用户登录成功后,通过HTTP响应头设置一个安全的Cookie,并向客户端返回JSON格式的成功响应。
Set-Cookie
字段:服务器通过该字段定义Cookie的内容。Path=/
:指定Cookie的作用路径(所有路径均可访问)。HttpOnly
:防止JavaScript访问Cookie(提高安全性)。Secure
:要求Cookie仅通过HTTPS传输(防止窃听)。
2. 浏览器存储
浏览器接收到Set-Cookie
后,会根据规则将Cookie存储到本地,并在后续请求中自动附加到请求头的Cookie
字段中。
浏览器存储规则:
- 域名匹配 :Cookie仅对指定的
Domain
生效(如example.com
)。 - 路径匹配 :Cookie仅对指定的
Path
生效(如/login
)。 - 安全策略 :若设置了
Secure
,Cookie仅通过HTTPS传输;若设置了HttpOnly
,JavaScript无法读取。
示例 : 当用户访问https://example.com/profile
时,浏览器会自动在请求头中附加Cookie:
vbnet
GET /profile HTTP/1.1
Host: example.com
Cookie: username=admin
3. 服务器读取
服务器通过解析请求头中的Cookie
字段,提取用户身份信息或其他数据。
示例:
vbnet
if (req.headers.cookie) { // 检查请求头中是否包含Cookie字段(即用户是否携带了Cookie)
// 1. 将Cookie字符串按"; "分割成多个键值对,再使用reduce遍历每个键值对,将其转换为对象格式
const cookies = req.headers.cookie.split('; ').reduce((acc, curr) => {
const [key, value] = curr.split('=');
acc[key] = value; // 4. 将键值对存入累加器对象(最终形成完整的Cookie对象)
return acc;
}, {});
if (cookies.username === 'admin') {
res.end('欢迎回来,管理员!');
else {
res.end('未找到用户信息');
}
} else {
res.end('请先登录');
}
这段代码可以解析HTTP请求中的Cookie信息,验证用户身份(如判断是否为"admin"),并根据验证结果返回不同的响应内容**。
2.2 Cookie的组成
用户可通过
f12
打开控制台,然后应用
->Cookie
->找到当前地址
,就能看到当前的Cookie信息。
Cookie是一个键值对字符串,通常包含以下属性:
1. name=value
:核心数据
-
作用:存储用户身份、偏好等信息。
-
代码:
iniSet-Cookie: username=admin
2. Expires/Max-Age
:过期时间
-
作用:决定Cookie何时失效。
-
区别:
Expires
:指定具体的过期日期(如Expires=Wed, 04-Jul-2025 12:00:00 GMT
)。Max-Age
:指定存活时间(单位为秒,如Max-Age=3600
表示1小时后过期)。
-
默认行为 :如果未设置,Cookie在浏览器关闭时失效,如:
会话Cookie
。
代码:
ini
Set-Cookie: username=admin; Max-Age=3600 // 设置Cookie在1小时后过期
3. Domain
:作用域
-
作用:指定Cookie所属的域名,允许跨子域名共享。
-
规则:
- 默认作用域为当前域名(如
example.com
)。 - 如果设置为
.example.com
,则所有子域名(如a.example.com
、b.example.com
)均可访问。
- 默认作用域为当前域名(如
代码:
ini
// 允许所有子域名访问该Cookie
Set-Cookie: username=admin; Domain=.example.com
4. Path
:路径范围
-
作用:指定Cookie生效的路径。
-
规则:
- 默认路径为当前请求的路径(如
/login
)。 - 如果设置为
/
,则整个网站均可访问。
- 默认路径为当前请求的路径(如
代码:
ini
// 整个网站均可访问该Cookie
Set-Cookie: username=admin; Path=/
5. HttpOnly
:安全性
-
作用 :防止JavaScript通过
document.cookie
读取或修改Cookie,防范XSS攻击。 -
代码:
ini// 禁止JavaScript访问该Cookie Set-Cookie: username=admin; HttpOnly
6. Secure
:传输安全
-
作用:要求Cookie仅通过HTTPS传输,防止中间人窃听。
-
代码:
ini// 仅通过HTTPS传输该Cookie Set-Cookie: username=admin; Secure
示例:完整Cookie设置
php
res.writeHead(200, {
'Set-Cookie': "username=admin; Expires=Wed, 04-Jul-2025 12:00:00 GMT; Path=/; Domain=example.com; HttpOnly; Secure",
'Content-Type': 'application/json'
});
res.end(JSON.stringify({ success: true, msg: '登录成功' }));
解析:
username=admin
:存储用户身份。Expires
:设置Cookie在2025年7月4日12:00过期。Path=/
:整个网站均可访问。Domain=example.com
:允许所有子域名共享。HttpOnly
:防止JavaScript窃取。Secure
:仅通过HTTPS传输。
注意事项
-
Cookie大小限制:
- 单个Cookie最大约4KB。
- 同一域名下浏览器通常限制存储50个Cookie。
-
Cookie数量限制:
- 浏览器对同一域名的Cookie数量有限制(如Chrome为200个)。
-
删除Cookie:
-
通过设置
Expires
为过去的时间或Max-Age=0
。 -
示例:
iniSet-Cookie: username=admin; Expires=Thu, 01-Jan-1970 00:00:00 GMT
-
-
安全建议:
- 永远不要在Cookie中存储敏感信息(如密码),改用Token或Session ID。
- 始终启用
HttpOnly
和Secure
,防止XSS和中间人攻击。
三、Cookie的实际应用场景
-
登录状态保持 : 当用户登录成功后,服务器通过
Set-Cookie
设置身份标识。浏览器会自动将此Cookie附加到后续请求中,服务器通过解析Cookie判断用户是否已登录。 -
购物车记录:用户将商品加入购物车时,可以通过Cookie临时存储商品ID,即使关闭浏览器后再次打开,购物车数据依然存在。
-
用户偏好设置:网站记录用户选择的主题(深色/浅色模式)、语言等偏好。
四、Cookie的优缺点与注意事项
4.1 优点
- 简单易用:通过HTTP头直接操作,无需额外库。
- 兼容性好:所有现代浏览器均支持。
4.2 缺点
- 大小限制:单个Cookie最大约4KB,总容量不超过5MB。
- 性能影响:每次请求都携带Cookie,可能增加传输数据量。
- 安全隐患 :如果未设置
HttpOnly
或Secure
,可能遭受XSS或CSRF攻击。
4.3 注意事项
- 合理设置过期时间:避免长期存储敏感信息。
- 加密敏感数据:如用户ID或令牌,防止被篡改。
- 结合其他技术 :对于复杂场景(如大型应用),建议使用
localStorage
或IndexedDB
补充存储。
五、附录:一段完整代码示例
前端代码
html
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Login Example</title>
<style>
body { font-family: Arial, sans-serif; }
#welcomeSection, #loginSection { display: none; margin-top: 50px; }
</style>
</head>
<body>
<!-- 欢迎信息部分 -->
<div id="welcomeSection">
<h1>Cookie</h1>
<p>Welcome, admin</p>
<button id="logoutButton">Logout</button>
</div>
<!-- 登录表单部分 -->
<div id="loginSection">
<form id="loginForm">
<input type="text" id="username" placeholder="Username" />
<input type="password" id="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
</div>
<script>
// 1. 获取 Cookie 的函数
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null;
}
// 2. 登录表单提交逻辑
document.getElementById('loginForm').addEventListener('submit', async (event) => {
event.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const response = await fetch('/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password })
});
const data = await response.json();
// 登录成功后显示欢迎信息
if (data.success) {
document.getElementById('welcomeSection').style.display = 'block';
document.getElementById('loginSection').style.display = 'none';
}
});
// 3. 页面加载时检测 Cookie
window.addEventListener('DOMContentLoaded', () => {
const sessionToken = getCookie('sessionToken');
if (sessionToken) {
// Cookie 存在:显示欢迎信息
document.getElementById('welcomeSection').style.display = 'block';
} else {
// Cookie 不存在:显示登录表单
document.getElementById('loginSection').style.display = 'block';
}
});
// 4. 登出按钮逻辑(可选)
document.getElementById('logoutButton')?.addEventListener('click', () => {
// 清除 Cookie(需确保后端允许前端删除)
document.cookie = 'sessionToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
location.reload(); // 刷新页面
});
</script>
</body>
</html>
后端代码
js
// server.js
const http = require('http');
const server = http.createServer((req, res) => {
// 处理登录请求
if (req.method === 'POST' && req.url === '/login') {
res.writeHead(200, {
'Set-Cookie': 'username=admin', // 设置Cookie
'Content-Type': 'application/json'
});
res.end(JSON.stringify({ success: true, msg: '登录成功' }));
}
// 检查登录状态
if (req.method === 'GET' && req.url === '/check-login') {
if (req.headers.cookie) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ loggedIn: true, username: 'admin' }));
} else {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ loggedIn: false, username: '' }));
}
}
});
server.listen(8080, () => {
console.log('Server running at http://localhost:8080');
});
4.3 运行效果
- 未登录时 :浏览器不会携带Cookie,服务器返回
loggedIn: false
,前端显示登录表单。
2. 登录成功后 :服务器设置Cookie,浏览器在后续请求中自动附加Cookie,服务器返回
loggedIn: true
,前端显示欢迎信息。
