基於上節課講的一些必要依賴之外,咱們須要安裝一下依賴:html
咱們依次建立,並實現基礎功能web
若有忘記,請參閱 Koa從零搭建到Api實現—項目的搭建
通常在涉及到密碼等敏感信息存儲數據庫時,不可能明文存儲,必須對其進行加密。而咱們最常使用的爲md5加密
簡單的密碼md5加密後能夠破解,可是稍微複雜一點的不可破解。故仍是很安全的算法
使用很簡單數據庫
const md5 = require('md5') md5(password)
一般咱們在用戶註冊時,獲取用戶錄入的密碼,加密後存到數據庫。而在登陸時,先對密碼加密,而後再與數據庫中加密後的數據進行匹配。json
{ 「typ」: 「JWT」, 「alg」: 「HS256」 }
由上可知,該token使用HS256加密算法,將頭部使用Base64編碼可獲得一串字符串安全
eyJhbGciOiJIUzI1NiJ91服務器
{ 「iss」: 「Online JWT Builder」, 「iat」: 1416797419, 「exp」: 1448333419, ……. 「userid」:10001 }
有效載荷中存放了token的簽發者(iss)、簽發時間(iat)、過時時間(exp)等以及一些咱們須要寫進token中的信息。有效載荷也使用Base64編碼獲得一串字符串app
eyJ1c2VyaWQiOjB91async
將Header和Playload拼接生成一個字符串str=「eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyaWQiOjB9」,使用HS256算法和咱們提供的密鑰(secret,服務器本身提供的一個字符串)對str進行加密生成最終的JWT,即咱們須要的令牌(token),形如:str.」簽名字符串」。ui
由上文所知,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())