定义:cookie指某些网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密)。
格式:cookie以键值对的形式存储于浏览器中,key=value。不同cookie之间用分号";"隔开, key1=value1;key2=value2;...;keyN=valueN
工作机制:当用户首次访问浏览器时,服务器将向浏览器写回一个cookie;在下一次访问服务器时,服务器则可以通过浏览器的cookie到服务器的session的存储空间中找到对应的数据。
注:如果cookie不设置过期时间,则默认在浏览器关闭时就删除
浏览器cookie截图一个域名下面可能存在着很多个cookie对象。cookie的多个属性列举如下:
- name字段为一个cookie的名称。
- value字段为一个cookie的值。
- domain字段为可以访问此cookie的域名。
非顶级域名,如二级域名或者三级域名,设置的cookie的domain只能为顶级域名或者二级域名或者三级域名本身,不能设置其他二级域名的cookie,否则cookie无法生成。
顶级域名只能设置domain为顶级域名,不能设置为二级域名或者三级域名,否则cookie无法生成。
二级域名能读取设置了domain为顶级域名或者自身的cookie,不能读取其他二级域名domain的cookie。所以要想cookie在多个二级域名中共享,需要设置domain为顶级域名,这样就可以在所有二级域名里面或者到这个cookie的值了。
顶级域名只能获取到domain设置为顶级域名的cookie,其他domain设置为二级域名的无法获取。- path字段为可以访问此cookie的页面路径。 比如domain是abc.com,path是/test,那么只有/test路径下的页面可以读取此cookie。
- expires/MaxAge 字段为此cookie超时时间。若设置其值为一个时间,那么当到达此时间后,此cookie失效。不设置的话默认值是Session,意思是cookie会和session一起失效。当浏览器关闭(不是浏览器标签页,而是整个浏览器) 后,此cookie失效。
- Size字段 此cookie大小。
- httpOnly字段 若此属性为true,前台则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie。
- secure 字段 设置是否只能通过https来传递此条cookie
NodeJS中使用cookie
-
搭建基本的服务器
-
处理响应的时候
(1) 声明全局的session global mySession = {};
(2) 处理登陆逻辑 /login ——完成登陆,记录用户状态,并且写回一个cookie
- 检验登陆逻辑,从数据库中获取用户信息
- 生成cookie值
- 关联cookie,存储到服务器的全局session中 global.mySession[cookieValue] = obj;
- 写回cookie
res.setHeader('set-cookie', 'cookie=cookieValue;[expires=过期时间][domain=允许访问的域名][;path=允许访问路径][;secure=约定传递方式][;httpOnly=布尔值][;size=大小]');
(3) 处理显示登陆状态的响应/result——通过cookie来获取,并且响应用户再session中的数据
- 从浏览器请求报文头中获取cookie字符串
req.headers.cookie
=> 'key1=value1;key2=value2;...' - 处理cookie字符串,取得存储用户状态的cookie值, 首先以";"作为关键字对字符串进行切割,再遍历数组元素,对每个cookie键对进行切割
- 从全局session中获取用户信息并写回浏览器
res.end(JSON.stringify(global.mySession[cookieValue]))
- 为保证安全性,cookie需要适当加密
依赖包jsonwebtokennpm install jsonwebtoken -S
(1) 对称加密
- 加密:
let token = jwt.sign({xxx: xxx}, 'shhhhh');
- 解密:
let decoded = jwt.verigy(token, 'shhhhh'); =>{xxx: xxx}
(2)非对称加密——公密私解 or 私密公解
- 读取公钥pub.pem文件进行加密
let cert = fs.readFileSync("./pub.pem");
let token = jwt.sign({xxx:xxx}, cert, {algorithm: 'RS256'});
- 读取私钥priv.pem文件进行解密
let cert = fs.readFileSync("./priv.pem");
let token = jwt.verify(token, cert, (err, decoded) {
//decoded = {xxx: xxx}
});
代码实现:
const http = require("http");
const fs = require("fs");
const path = require("path");
const url = require("url");
const jwt = require("jsonwebtoken"); //加密依赖包
//模拟一个session
global.mySession ={};
//模拟一个用户列表
let myUserlist = [
{
id: 0,
username: 'jack'
},
{
id: 1,
username: 'john'
}
];
let pub = fs.readFileSync('./public.key'); //读取公钥文件
let priv = fs.readFileSync('./private.key'); //读取私钥文件
const server = http.createServer((req, res) => {
if(req.url.startsWith('/user_login')) { //登陆操作
//获取请求报文的参数
let query = url.parse(req.url, true).query;
//遍历用户列表(数据库),得到用户id(严格来讲,这里还需要各种校验),返回token
for(let i = 0; i < myUserlist.length; i ++) {
if(myUserlist[i].username.toLowerCase() === query.username.toLowerCase()) {
//使用私钥生成一个加密的cookie值
let cookieValue = `${myUserlist[i].id}_${Date.now()}`;
let token = jwt.sign(cookieValue, priv, {algorithm: 'RS256'}); //最后一个参数可省
//关联cookie值,存入到全局的session中
global.mySession[cookieValue] = {name: query.username};
//写回cookie给前台
res.setHeader('set-cookie', `id=${token}`);
res.end();
}
}
} else if (req.url.startsWith('/result')) { //同一域名下的不同页面,可以通过cookie共享数据
//从前台页面中得到token值
let cookies = req.headers.cookie.split(";").map(c => c.split("=")); //[[cookie1, value1], [cookie2, value2], ...]
let user;
cookies.forEach(c => {
if(c[0].trim() === 'id') {
//使用公钥解密cookie,并从session中读取对应的值
jwt.verify(c[1], pub, (err, decoded) => {
user = global.mySession[decoded];
});
}
})
//返回页面
fs.readFile('./result.html', (err, data) => {
res.writeHeader(200);
res.end(data.toString().replace('{{user}}', JSON.stringify(user)));
})
} else if (req.url.startsWith('/login')) { //登陆页面
//返回加载页面
fs.readFile('./login.html', (err, data) => {
res.writeHeader(200);
res.end(data);
})
}
});
server.listen(8888, () => {
console.log("The server is running...");
})
网友评论