Token做爲用戶的惟一標識,他的做用不過多去說,本文帶你從Token生成到驗證全過程!html
基於上節課講的一些必要依賴之外,咱們須要安裝一下依賴:web
咱們依次建立,並實現基礎功能算法
若有忘記,請參閱Koa從零搭建到Api實現—項目的搭建數據庫
通常在涉及到密碼等敏感信息存儲數據庫時,不可能明文存儲,必須對其進行加密。而咱們最常使用的爲md5加密 簡單的密碼md5加密後能夠破解,可是稍微複雜一點的不可破解。故仍是很安全的json
使用很簡單安全
const md5 = require('md5')
md5(password)
複製代碼
一般咱們在用戶註冊時,獲取用戶錄入的密碼,加密後存到數據庫。而在登陸時,先對密碼加密,而後再與數據庫中加密後的數據進行匹配。bash
{
「typ」: 「JWT」,
「alg」: 「HS256」
}
複製代碼
由上可知,該token使用HS256加密算法,將頭部使用Base64編碼可獲得一串字符串服務器
eyJhbGciOiJIUzI1NiJ91app
{
「iss」: 「Online JWT Builder」,
「iat」: 1416797419,
「exp」: 1448333419,
…….
「userid」:10001
}
複製代碼
有效載荷中存放了token的簽發者(iss)、簽發時間(iat)、過時時間(exp)等以及一些咱們須要寫進token中的信息。有效載荷也使用Base64編碼獲得一串字符串async
eyJ1c2VyaWQiOjB91
將Header和Playload拼接生成一個字符串str=「eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOjB9」,使用HS256算法和咱們提供的密鑰(secret,服務器本身提供的一個字符串)對str進行加密生成最終的JWT,即咱們須要的令牌(token),形如:str.」簽名字符串」。
由上文所知,token是咱們根據Base64編碼生成的,反之咱們對其進行解析,解析失敗即token不合法
const md5 = require('md5')
const jwt = require('jsonwebtoken')
const secret = require('../config/secret.json')
const login = async (obj) => {
try {
if (!obj.name || !obj.password) {
return {
err: '帳號和密碼不能爲空',
success: false
}
} else {
const name = await db.user.findAll({where: {name: obj.name}})
// 密碼加密
obj.password = md5(obj.password)
// 判斷用戶是否存在
if (name.length <= 0) {
return {success: false, err: '當前用戶不存在'}
} else {
let results = await db.user.findOne({where: obj})
// 驗證用戶密碼
if (results) {
// 寫入token的信息
const userToken = {
name: results.name,
id: results.id
}
// 簽發Token 1小時後過時
const token = jwt.sign(userToken, secret.sign, {expiresIn: '1h'})
return {
success: true,
token: token,
data: results
}
} else {
return {
success: false,
err: '密碼錯誤'
}
}
}
}
} catch (e) {
console.log(e)
return {
success: false,
err: '登陸失敗,請聯繫管理員...'
}
}
}
module.exports = {
login
}
複製代碼
咱們在接口中生成token,將token返回客戶端,客戶端應在請求時攜帶此token,服務端根據token驗證其身份。
在Koa中,咱們能夠經過中間件的方式來實現請求攔截。
const jwt = require('jsonwebtoken')
const secret = require('../config/secret.json')
const util = require('util')
const verify = util.promisify(jwt.verify)
module.exports = function () {
return async function (ctx, next) {
// 設置接口白名單,不進行token驗證
if (ctx.url === '/user/login') {
await next()
} else {
try {
let token = ctx.header.authorization // 獲取請求攜帶的token
if (token) {
let payload
try {
payload = await verify(token.split(' ')[1].replace(/\s/g,''), secret.sign) // 解密payload,獲取用戶名和ID
// 驗證 合法性
// if (payload.exp <= new Date()/1000) {
// console.log('過時了')
// }
await next()
} catch (err) {
ctx.body = {success: true, message: '登陸密鑰失效或過時,請從新登陸', code: -1}
console.log('token verify fail: ', err)
}
} else {
ctx.body = {success: false, message: '驗證失敗', code: -1}
}
} catch (err) {
console.log(err)
}
}
}
}
複製代碼
最後將中間件進行引入使用
必定在router以前
app.use(token())
app.use(router.routes())
複製代碼