JWT認(rèn)證機(jī)制
session不可跨域械姻,用戶信息保存在服務(wù)器端;
JWT可以跨域恩脂,用戶信息保存在瀏覽器帽氓;
JWT工作原理
1、客戶端通過瀏覽器輸入密碼用戶名發(fā)起登陸請求俩块;
2黎休、服務(wù)器驗證后,對用戶信息通過加密后生成token字符串響應(yīng)給客戶端(服務(wù)器不保留token字符串)玉凯;
3势腮、將加密后token字符串存儲到local storage;
4、客戶端再次發(fā)起請求時漫仆,會把token字符串發(fā)送給服務(wù)器端捎拯;
5、服務(wù)器端收到token字符串后盲厌,會還原成客戶用戶信息開始驗證署照,驗證成功后會根據(jù)當(dāng)前用戶生成特定的響應(yīng)內(nèi)容祸泪;
安裝JWT相關(guān)的包
1、jsonwebtoken:用于生成JWT字符串建芙;
2没隘、express-jwt:用于將JWT字符串還原為JSON對象;
npm i jsonwebtoken express-jwt
3禁荸、導(dǎo)入jsonwebtoken express-jwt 模塊
// TODO_01:安裝并導(dǎo)入 JWT 相關(guān)的兩個包升略,分別是 jsonwebtoken 和 express-jwt
const jwt = require('jsonwebtoken') //用于生成token字符串
const expressJWT = require('express-jwt') //用于將token字符串還原成json對象
4、定義secret密鑰屡限,對JWT字符串進(jìn)行加密品嚣;
// TODO_02:定義 secret 密鑰,建議將密鑰命名為 secretKey
const secretKey = 'itheima No1 ^-^'
5钧大、在登錄成功之后翰撑,調(diào)用 jwt.sign() 方法生成 JWT 字符串。并通過 token 屬性發(fā)送給客戶端
// 登錄接口
app.post('/api/login', function (req, res) {
// 將 req.body 請求體中的數(shù)據(jù)啊央,轉(zhuǎn)存為 userinfo 常量
const userinfo = req.body
// 登錄失敗
if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
return res.send({
status: 400,
message: '登錄失斂粽!'
})
}
// 登錄成功
// TODO_03:在登錄成功之后瓜饥,調(diào)用 jwt.sign() 方法生成 JWT 字符串逝撬。并通過 token 屬性發(fā)送給客戶端
// 參數(shù)1:用戶的信息對象
// 參數(shù)2:加密的密鑰
// 參數(shù)3:配置對象,可以配置當(dāng)權(quán) token 的有效期 30s 30秒過后失效
const tokenStr = jwt.sign({ username: userinfo.username},secretKey, { expiresIn: '30s'})
res.send({
status: 200,
message: '登錄成功乓土!',
token: 'tokenStr' // 要發(fā)送給客戶端的 token 字符串
})
})
6宪潮、注冊將 JWT 字符串解析還原成 JSON 對象的中間件;
// TODO_04:注冊將 JWT 字符串解析還原成 JSON 對象的中間件
// expressJWT({secret:'secretKey'}) 就是用來解析 token 的中間件
// .unless({path: [/^\/api/] }) 用來指定哪些接口不需要訪問權(quán)限
// 注意:只要配置成功了express-jwt 這個中間件趣苏,就可以解析出來的用戶信息狡相,掛載到req.user屬性上
app.use(expressJWT({secret: secretKey, algorithms:['HS256'] }).unless({path: [/^\/api/] }))
6、使用 req.user 獲取用戶信息食磕,并使用 data 屬性將用戶信息發(fā)送給客戶端
// 這是一個有權(quán)限的 API 接口
app.get('/admin/getinfo', function (req, res) {
// TODO_05:使用 req.user 獲取用戶信息尽棕,并使用 data 屬性將用戶信息發(fā)送給客戶端
console.log(req.user)
res.send({
status: 200,
message: '獲取用戶信息成功!',
data: req.user // 要發(fā)送給客戶端的用戶信息
})
})
9彬伦、錯誤中間件
// TODO_06:使用全局錯誤處理中間件滔悉,捕獲解析 JWT 失敗后產(chǎn)生的錯誤
app.use((err, req, res, next) => {
// 這次錯誤是由 token 解析失敗導(dǎo)致的
if(err.name === 'UnauthorizedError') {
return res.send({
status: 401,
message: '無效的token'
})
res.send({
status: 500,
message: '未知的錯誤'
})
}
})