# 从 Cookie 说起
# 什么是 cookie ?
- 存储在浏览器端的一串字符串(最大 5kb)
- 跨域不共享。浏览器可为每个域名以及域名下的 每个 path 存储一个 cookie,各域名之间不共享,path 下的 cookie 权限,小范围可访问大范围。
- 结构化数据:key1=val1; key2=val2; key3=val3; 注意是通过 '; '连接的
- 浏览器环境下可以修改 cookie,但是可以进行限制,限制的目的也是为了不让修改 cookie
- 每次的 Http 请求会自动将当前请求域的 cookie 一起发送到服务端
- server 端可获取并修改 cookie 然后返回浏览器
- 获取:cookie 到了服务端默认在 req.headers.cookie 中
- 设置:res.setHeader('Set-Cookie',
key=value; path=/; httpOnly; expires=时间
) - path 不设置时,会默认将 cookie 设置到当前访问的 path 下
- httpOnly 的设置是限制 cookie 在浏览器端不能被修改的
- expires 是设置 cookie 到期的时间,要通过 toGMTString() 方法将时间转成 GMT 形式
# 浏览器修改 cookie
- 获取 cookie: document.cookie
- 设置 cookie: document.cookie = 'key1=value1' 会自动使用字符串方式拼接到现有 cookie 上
# 服务端操作 cookie
测试时,可以手动在浏览器端通过 document.cookie 添加 cookie,然后刷新页面尝试
const http = require("http");
const server = http.createServer((req, res) => {
// cookie 默认存在 req.headers.cookie
// 形式 k1=v1; k2=v2;
// 将 cookie 存储在 req 中
req.cookie = {};
const cookieStr = req.headers.cookie || "";
cookieStr.split(";").forEach((item) => {
const [key, val] = item.split("=");
req.cookie[key] = val;
});
res.end(JSON.stringify(req.cookie));
});
server.listen(7788, () => {
console.log("server is running at port 7788");
});
# Cookie 模拟登录与验证
- cookie 在服务端的设置是通过 res.setHeader 中的 Set-Cookie 来处理的
const http = require("http");
const querystring = require("querystring");
const server = http.createServer((req, res) => {
res.setHeader("Content-type", "application/json; text/plain; charset=utf-8");
// cookie 默认存在 req.headers.cookie
// 形式 k1=v1; k2=v2;
// 将 cookie 存储在 req 中
req.cookie = {};
const cookieStr = req.headers.cookie || "";
cookieStr.split(";").forEach((item) => {
const [key, val] = item.split("=");
req.cookie[key] = val;
});
// query 参数
const [path, query] = req.url.split("?");
req.path = path;
req.query = querystring.parse(query);
if (path === "/login-in") {
const { username, password } = req.query;
// 修改(设置) cookie
// path 指明 cookie 设置的 path 路径,不设置的话,会默认设置到当前的 path 上
res.setHeader("Set-Cookie", `user=${username}; path=/`);
res.end(
JSON.stringify({
username,
password,
msg: "欢迎登录, cookie设置成了user=username",
})
);
return;
}
// 登录后,浏览器端有了 cookie,再次发送请求会从 req.headers.cookie 中获取到
if (path === "/login-test") {
res.end(
JSON.stringify({
username: req.cookie.user,
msg: "已登录,从请求中获取到了cookie.user",
})
);
}
});
server.listen(7788, () => {
console.log("server is running at port 7788");
});
# Cookie 的限制
cookie 本身可以在浏览器端通过 document.cookie 进行获取和修改。如果涉及登录信息,就没有任何安全了。所以需要对 cookie 的修改做限制,让它只能在服务端被修改。 这就需要在服务端返回设置 cookie 的 setHeader 中做处理了。
const http = require("http");
const querystring = require("querystring");
// 设置 cookie 过期时间
function setCookieExpires() {
const d = new Date();
// 在当前时间加上24小时
d.setTime(d.getTime() + 24 * 60 * 60 * 1000);
// toGMTString 的格式:Sat, 11 Dec 2021 04:51:51 GMT
return d.toGMTString();
}
const server = http.createServer((req, res) => {
res.setHeader("Content-type", "application/json; text/plain; charset=utf-8");
// cookie 默认存在 req.headers.cookie
// 形式 k1=v1; k2=v2;
// 将 cookie 存储在 req 中
req.cookie = {};
const cookieStr = req.headers.cookie || "";
cookieStr.split("; ").forEach((item) => {
const [key, val] = item.split("=");
req.cookie[key] = val;
});
// query 参数
const [path, query] = req.url.split("?");
req.path = path;
req.query = querystring.parse(query);
if (path === "/login-in") {
const { username, password } = req.query;
// 修改(设置) cookie
// path 指明 cookie 设置的 path 路径,不设置的话,会默认设置到当前的 path 上
// httpOnly 指明 cookie 只能在服务端修改
// expires 设置 cookie 过期时间
res.setHeader(
"Set-Cookie",
`user=${username}; path=/; httpOnly; expires=${setCookieExpires()}`
);
res.end(
JSON.stringify({
username,
password,
msg: "欢迎登录, cookie设置成了user=username",
})
);
return;
}
// 登录后,浏览器端有了 cookie,再次发送请求会从 req.headers.cookie 中获取到
if (path === "/login-test") {
res.end(
JSON.stringify({
username: req.cookie.user,
msg: "已登录,从请求中获取到了cookie.user",
})
);
}
});
server.listen(7788, () => {
console.log("server is running at port 7788");
});
经过上述在 Set-Cookie 中添加 httpOnly。当通过访问 localhost:7788/login-in?username=hzz&password=123 模拟登录后,再通过在浏览器控制台手动将 cookie 设置成别的,如:document.cookie='user=lisi', 再次访问 localhost:7788/login-test 校验登录信息查看时发现 cookie 保持为登录时的 hzz 不变。