項目中凡是涉及到用戶登陸註冊的都須要一個登陸態來驗證用戶的登陸狀態,經常使用的登陸臺無外乎是token、session啊這些標識。這裏我使用的是token字段。token通常會包含用戶的我的信息,如:帳號、帳號id、用戶名等等,更爲安全的是加入一個自定義的鹽(salt)一塊兒加密,防止用戶信息泄漏。下面就一塊兒來使用一下:前端
說到token,確定會想到後端是怎麼知道前端給個人token是否是我傳給他的有效值呢?就是說後端須要有個值去跟前端傳過來的token進行比較才知道合法性。因此後端在生成token的同時本身也須要將這個生成的token存下來備用。我這裏選用的redis。它是一個數據庫,以鍵值對的形式存儲,這樣我就能夠將生成的token存儲下來了。至於redis的安裝和部署這裏就不累贅了,這裏直接將使用。
在項目根目錄下新建一個redis文件夾。其下新建一個redis.js文件。
web
//redis.js const Redis = require('ioredis')//導入模塊 const redis = { port: 6379, // Redis port host: '127.0.0.1', // Redis host prefix: '***', //存諸前綴 ttl: 60 * 60 * 24 * 7 * 1000, //過時時間 family: 4, db: 0 } const redisClient = new Redis(redis) //導出備用 module.exports = redisClient
這樣redis就可使用了。redis
本文使用jsonwebtoken進行加密和解密。
由於加密和解密是不少接口都須要用的東西,因此我將這些方法寫到公共的部分去。
utils文件下建一個common.js,用來存放公共的方法。
數據庫
//common.js const secret = require('./secret')//導入自定義的鹽 const jwt = require('jsonwebtoken')//導入jsonwebtoken const verify = util.promisify(jwt.verify) // token解密 const common = { //定義一個對象 //加密 //後端生成惟一的key /* * paylod:包含來用戶的信息 * secret.secret 自定義的鹽(salt) * expiresIn 設置這個token的有效期 */ //jwt.sign是jsonwebtoken模塊的一個方法,能夠將傳入的信息加密 getToken(paylod, expiresIn) { return jwt.sign(paylod, secret.secret, expiresIn) }, //解密 //根據收到的token獲取用戶信息 getUserInfo(token) { return verify(token, secret.secret) }, } //導出這個對象給外部使用 module.exports=common
新建一個secret.js文件用來存放自定義的信息npm
⚠️ 這裏建議鹽最好亂寫 寫得越亂越好,各類字符都加上,並亂序寫。
json
加密後端
咱們在用戶登陸成功的時候將用戶信息加密。因此我在個人管理員登陸接口那寫。api
個人在routes文件下的admin.js文件安全
//admin.js const router = require('koa-router')() const api = require('../controllers/api') const redisClient = require('../redis/redis.js') const common = require('../util/comon') router.prefix('/admin') //管理員登陸 router.post('/userLogin', async (ctx, next) => { /*寫你的接口邏輯*/ //定義一個用戶信息對象 const paylod = { name: '登陸用戶的用戶名', userid: '登陸用戶的id',//登陸時可查表查拿到用戶id author: '13414851033@163.com', type:'***', timestamp: new Date()//加個時間戳保證加密後token的惟一性 } /*核心代碼*/ // 調用上面公共的token加密方法(注:這裏是沒有傳鹽進去的,我是直接在common文件引入來鹽) // expiresIn設置token的有效期是7天 const token = await common.getToken(paylod, { expiresIn: '7 days' }) //每次登陸以前先清除掉全部的有關此用戶的key(根據用戶id) let preToken = await redisClient.get(result.userid) //這個preToken就是當初登陸時redis存下來的key await redisClient.del(preToken) //用token做爲key、自定義的token前綴+token做爲值 傳key給前端做爲校驗 await redisClient.set(token, secret.identif + token) //再生成一對鍵值對 用來記錄是屬於哪一個用戶的token 用戶id做爲key 傳給前端的token(上一條鍵值對的key)做爲值 await redisClient.set(result.userid,token) ctx.body = { status: 200, code: 200, message: '登陸成功', data: result, token: token//將token傳給前端 } }
這樣登陸成功後的話前端就能收到後端生成的惟一性的token了,同時我也生成了兩對的鍵值對。一對是以token爲key,以自定義的token前綴爲value;一對是以用戶id爲key,以token做爲值的數據。在用戶登陸時拿到用戶的id,在redis中清除掉以這個用戶id爲key的記錄,再存入一條以token爲key的記錄。這樣就能保證每次用戶登陸該用戶都是隻有一個合法的key(就是所謂的同一個帳戶在多地登陸會擠掉其餘人的登陸狀態)。session
解密
加密完以後客戶端請求必然須要帶上登陸態token來操做數據,可是不可能在客戶端去傳用戶的數據,那樣太不安全了,這樣我上面生成token時將用戶信息加進去的數據就有用處了,只要解密我就能知道這個token所攜帶的用戶信息了。這個token客戶端看到也是不知道用戶信息的,因此相對來講比較安全些。
在common.js寫了一個獲取前端傳入的token(走請求頭傳入,不以參數的形式)
//common.js //根據請求頭的信息獲取前端傳入的token getHeaderToken(ctx) { if (ctx.header && ctx.header.token) { return ctx.header.token } }
const common = require('../util/comon') //刪除管理員 router.post('/****', async (ctx, next) => { let token = await common.getHeaderToken(ctx) let userInfo = await common.getUserInfo(token) //用戶名 console.log(userInfo.name) //用戶id console.log(userInfo.userid) }
因此,只要前端傳入token,後端就能知道這個token所攜帶的用戶的信息,方便後端處理數據所需的必備條件。
以上就是本此介紹token的使用,下文將介紹根據登陸臺控制接口請求權限。
上一篇:編寫接口路由
下一篇:token控制接口權限