node中使用cookie
cookie
以下示例说明了安装和加载 cookie 解析中间件功能cookie-parser
。
$ npm install cookie-parser
var express = require('express')
var app = express()
var cookieParser = require('cookie-parser')
// load the cookie-parsing middleware
app.use(cookieParser([signed]))
-
设置cookie:需要signed签名
会在响应头中添加
Set-Cookie: key=value; Path=/
res.cookie('key','value', option)
其中option要求是要json格式:有以下选项
-
domain: 域名。设置子域名(二级域名)是否可以访问cookie。 例:domain:’.主域名’ name=value:键值对,可以设置要保存的 Key/Value,注意这里的 name 不能和其他属性项的名字一样
-
expires: 过期时间(秒),在设置的某个时间点后该 Cookie 就会失效,如 expires=Wednesday, 09-Nov-99 23:12:40 GMT
-
maxAge: 最大失效时间(毫秒),设置在多少后失效
-
secure: 当 secure 值为 true 时, cookie 在 HTTP 中是无效,在 HTTPS 中才有效
-
path: 表示 cookie 影响到的路由,如 path=/。如果路径不能匹配时,浏览器则不发送这个 Cookie
-
httpOnly:默认为false,建议设置为true, 客户端将无法通过document.cookie读取到 COOKIE 信息,可防止 XSS 攻击产生
-
signed: 表示是否签名(加密) cookie, 设为 true 会对这个 cookie 签名,这样就需要用res.signedCookies 访问它,前提需要设置上面中间件app.use传参 。未签名则用 res.cookies 访问
被篡改的签名 cookie 会被服务器拒绝,并且 cookie值会重置为它的原始值
-
-
读取cookie值:客户端请求时 每次会在请求头中携带cookie:
Cookie: key=value
app.get('/', function (req, res) { // Cookies that have not been signed console.log('Cookies: ', req.cookies); // 对象集合 // Cookies that have been signed console.log('Signed Cookies: ', req.signedCookies) })
session
cookie 虽然很方便,但是使用 cookie 有一个很大的弊端,cookie 中的所有数据在客户端就可以被修改,数据非常容易被伪造,那么一些重要的数据就不能存放在 cookie 中了,而且如果 cookie 中数据字段太多会影响传输效率。为了解决这些问题,就产生了 session,session 中的数据是保留在服务器端的。
session 的运作通过一个 session_id
来进行。session_id
通常是存放在客户端的 cookie 中,比如在 express 中,默认是 connect.sid
这个字段,当请求到来时,服务端检查 cookie 中保存的 session_id 并通过这个 session_id 与服务器端的 session data 关联起来,进行数据的保存和修改。
这意思就是说,当你浏览一个网页时,服务端随机产生一个 1024 比特长的字符串,然后存在你 cookie 中的 connect.sid
字段中。当你下次访问时,cookie 会带有这个字符串,然后浏览器就知道你是上次访问过的某某某,然后从服务器的存储中取出上次记录在你身上的数据。由于字符串是随机产生的,而且位数足够多,所以也不担心有人能够伪造。伪造成功的概率比坐在家里编程时被邻居家的狗突然闯入并咬死的几率还低。
session 可以存放在
1)内存
2)cookie本身
3)redis 或 memcached 等缓存中
4)数据库中
线上来说,缓存的方案比较常见,存数据库的话,查询效率相比前三者都太低,不推荐;
express 中操作 session 要用到 express-session
(https://github.com/expressjs/session ) 这个模块,主要的方法就是 session(options)
,其中 options 中包含可选参数,主要有:
- name: 设置 cookie 中,保存 session 的字段名称,默认为
connect.sid
。 - store: session 的存储方式,默认存放在内存中,也可以使用 redis,mongodb 等。express 生态中都有相应模块的支持。
- secret: 通过设置的 secret 字符串,来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改。
- cookie: 设置存放 session id 的 cookie 的相关选项,默认为
- (default: { path: ‘/’, httpOnly: true, secure: false, maxAge: null })
- genid: 产生一个新的 session_id 时,所使用的函数, 默认使用
uid2
这个 npm 包。 - rolling: 每个请求都重新设置一个 cookie,默认为 false。
- resave: 即使 session 没有被修改,也保存 session 值,默认为 true。
-
安装
npm install express-session npm install cookie-parser
-
引包
var session = require("express-session"); var cookieParser = require("cookie-parser")
-
开启cookie并配置session
app.use(cookieParser()) app.use(session({ // 设置签名秘钥 内容可以任意填写 secret:"dsafsafsf", // 设置cookie的过期时间,例:30s后 session和相应的cookie失效过期 cookie:{ maxAge:30*1000 }, // 强制保存,如果session没有被修改也要重新保存 resave:true, // 如果原先没有session 那么就设置,否则不设置 saveUninitialized:false }))
-
使用session存储数据
app.get("/session/create",function(req,res){ // 往session里存储数据 req.session.name = 'jack'; //loginok:可以是任意内容,可以为true或false res.send("添加成功") })
-
读取session存储数据
app.get("/session/read",function(req,res){ // 查看session console.log(req.session) res.send("查询成功") })
-
删除session中的数据
app.get("/session/delete",function(req,res){ req.session.destroy(); res.redirect("/"); // 删除成功后转到百度页面 res.send("删除成功") })
token
-
什么是Token?
Token指访问资源的凭据,是一种身份认证的方式,它是解决跨域认证的最流行的一种方式。
-
为什么用Token?
以前较为流行的是通过session去做身份认证,session是通过服务器中保存会话数据来做身份认证,这种方式会导致在高并发中服务器压力过大的情况,还有就是,如果是服务器集群,那么就需要这些服务器session共享。
Token不在服务器中保存会话数据,而是保存在客户端。每次请求的headers中存入Token,在服务器中判断Token的有效性,是否可以访问资源。
-
传统Token和JWT的区别
-
传统Token
用户发起登录请求,登录成功之后返回Token,并且存于数据库,用户访问资源的时候需要携带Token,服务端获取Token之后和数据库中的对比。
-
JWT
用户发起登录请求,登录成功之后返回Token,但是不存于数据库,用户访问资源的时候需要携带Token,服务端获取Token之后去校验Token的合法性。
-
JWT分为三个部分header、payload、verify signature
-
header
内部包含有签名算法、Token类型,然后通过base64url算法转成字符串
// 明文例子: { "alg":"HS256", "typ":"JWT" }
-
payload
内部包含JWT标准数据和自定义数据,然后通过base64url算法转成字符串
JWT标准数据常见的有:
- iss:提供方。
- sub:主题,一般是用户ID。
- exp:过期时间。
- iat:创建时间。
- jti:token的唯一标识。
可选择性使用以上标准数据
// 明文例子: { "id": 3, "name": "Bmongo", "age": 18, "iat": 1588139323, "exp": 1588139333 }
注意:由于JWT是默认不加密的,所以在这边不要存敏感信息
-
verify signature
这部分是对前两部分的签名,防止数据的篡改
secret是服务器端保存的密钥,只有服务器端知道,再使用header中所指定的签名算法对上面的俩部分进行签名,按照以下公式生成签名
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
算出签名之后,把三部分通过.分隔开返回给用户就行了
JWT例子:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTAsIm5hbWUiOiLlvKDkuIkiLCJhZ2UiOjE2LCJpYXQiOjE1ODgxMzkzMjMsImV4cCI6MTU4ODEzOTMzM30.WzZp_aNgiw4iTsX7buxMhZe0z0e94Ve6ImEZ8L8L78c
-
客户端请求
每次客户端的请求都需要带上这个
token
,一般是把token写入到请求的headers中
使用express-jwt
和jsonwebtoken
实现token的编码与解析:
- jsonwebtoken 主要用到两个方法,一个是生成token的sign方法,还有一个时验证token的verify方法
-
安装
$ npm i express-jwt jsonwebtoken -S
-
引包
const express = require("express"); const jwt = require("jsonwebtoken"); const expressJwt = require("express-jwt");
-
配置
const app = express(); app.use(expressJwt({ secret: "secret" // 密钥,用来生成Signature algorithms: ['HS256'], // 加密算法 }).unless({ path: ["/login"] // 添加不需要token的接口 }));
app.use(function(err, req, res, next) { // 检测未携带token请求接口 if (err.name === "UnauthorizedError") { res.status(401).send(err); } });
-
生成token
let authToken = jwt.sign({ username, password }, "secret", { expiresIn : 60*60*24 // 授权时效24小时 }); // 返回客户端 res.status(200).json({ success : true, message : "登陆成功." token: authToken });
-
验证token
let token = req.headers.token; // 存入头部的信息 //用 加密密钥 解密,获取验证结果 jwt.verify(token, "secret", function(err, decoded) { // 如果超时 或者解码失败 则会返回错误对象 if (err) { res.status(200).json(err) } else {// 返回payload信息以及生成和失效时间 //{ "uname": "张三", "pwd": "abc123", "iat": 1633969144, "exp": 1634055544 } res.status(200).json(decoded); } })