1.前言
- 登錄狀態(tài)校驗(yàn):采用Token機(jī)制實(shí)現(xiàn)無(wú)狀態(tài)認(rèn)證
- 登錄時(shí):生成Token返回客戶端的同時(shí),服務(wù)端也存儲(chǔ)一份,將來(lái)用于主動(dòng)踢人
- 校驗(yàn)API時(shí):通過(guò)中間件對(duì)客戶端Token進(jìn)行解包校驗(yàn),還有服務(wù)端校驗(yàn)
2.密碼管理
- 登陸時(shí),前端明文傳輸,后端加密后存儲(chǔ)(一來(lái)通過(guò)HTTPS協(xié)議,即使數(shù)據(jù)被攔截也無(wú)法輕易讀取,二來(lái)由服務(wù)端負(fù)責(zé)哈希處理更具安全性和靈活性)
- 設(shè)定密碼,使用bcryptjs,對(duì)密碼進(jìn)行加密后進(jìn)行存儲(chǔ)
- 校驗(yàn)密碼,使用bcryptjs,拿前端提交的明文密碼和數(shù)據(jù)庫(kù)已經(jīng)存儲(chǔ)的hash密碼進(jìn)行校驗(yàn)
//引入
const bcrypt = require('bcryptjs');
//生成hash密碼
async function hashPassword(password) {
// 自動(dòng)生成一個(gè)鹽,默認(rèn)因子為10
const salt = await bcrypt.genSalt(10)
// 使用鹽來(lái)哈希密碼
const hashedPassword = await bcrypt.hash(password, salt);
return hashedPassword;
}
//對(duì)比明文密碼和已經(jīng)存儲(chǔ)的hash密碼進(jìn)行
const isMatch = await bcrypt.compare("登陸時(shí)的明文密碼", "已經(jīng)存儲(chǔ)的hash密碼進(jìn)行")
- 生成hash密碼時(shí),需要用到salt,比對(duì)密碼時(shí),不需要salt了嗎?:bcrypt 在生成哈希值時(shí),會(huì)將使用的salt含在最終的哈希字符串中, 所以比對(duì)密碼時(shí),不再需要salt
3.token管理
- 生成token:登錄成功后,根據(jù)用戶id和過(guò)期信息來(lái)生成token,并將token存入數(shù)據(jù)庫(kù),然后返回給前端
- 前端接收:前端接收token,并進(jìn)行存儲(chǔ),每次發(fā)起請(qǐng)求則帶上這個(gè)token
- 校驗(yàn)token:后端處理請(qǐng)求前,對(duì)token進(jìn)行解包處理,得到原信息,再核對(duì)數(shù)據(jù)庫(kù)的token隊(duì)列校驗(yàn)是否通過(guò)
- 刪除token:用戶修改密碼后,需要將之前已經(jīng)下發(fā)的token進(jìn)行失效處理,這也是為什么將token存入數(shù)據(jù)庫(kù)的原因之一,單靠jwt是不夠的
- 生成token的庫(kù):jsonwebtoken
//token管理工具
var jwt = require('jsonwebtoken')
//密鑰
var privateKey = "xxx"
//模擬登錄信息
var loginData = {
id: 1,
password: "123456"
}
//模擬登錄成功(校驗(yàn)密碼環(huán)節(jié)省略)
//生成token返回(包含人員信息和過(guò)期信息)
var token = jwt.sign({
userId: loginData.id,//人員id
expires: 1737092833417,//過(guò)期時(shí)間,這里應(yīng)該動(dòng)態(tài)生成
}, privateKey)
//eyJhbGciOiJIUzI1NiJ9.MTIzNDU2.6j-7-PqBNNVHeOYuI5L4lmSX7rDudiuH-ejSWriUb1g
console.log(token)
//將token存到數(shù)據(jù)庫(kù),再后端添加控制
var tokenList = []
tokenList.push(token)
//后期請(qǐng)求接口,解析token
try {
//解包token,拿到用戶信息和過(guò)期時(shí)間 { userId: 1, expires: 1737092833417, iat: 1737092957 }
var decoded = jwt.verify(token, privateKey)
//拿到用戶id,從數(shù)據(jù)庫(kù)中查詢?cè)撚脩鬷d對(duì)應(yīng)的token
//如果不存在,則說(shuō)明該token已經(jīng)失效
//如果存在,則校驗(yàn)是否已經(jīng)過(guò)期
} catch (error) {
//無(wú)效的token進(jìn)入這里
console.log('token校驗(yàn)失敗')
}