專案中凡是涉及到使用者登入註冊的都需要一個登入態來驗證使用者的登入狀態,常用的登入臺無外乎是token、session啊這些標識。這裡我使用的是token欄位。token一般會包含使用者的個人資訊,如:賬號、賬號id、使用者名稱等等,更為安全的是加入一個自定義的鹽(salt)一起加密,防止使用者資訊洩漏。下面就一起來使用一下:
說到token,肯定會想到後端是怎麼知道前端給我的token是不是我傳給他的有效值呢?就是說後端需要有個值去跟前端傳過來的token進行比較才知道合法性。所以後端在生成token的同時自己也需要將這個生成的token存下來備用。我這裡選用的redis。它是一個資料庫,以鍵值對的形式儲存,這樣我就可以將生成的token儲存下來了。至於redis的安裝和部署這裡就不累贅了,這裡直接將使用。
在專案根目錄下新建一個redis資料夾。其下新建一個redis.js檔案。
//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就可以使用了。
本文使用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檔案用來存放自定義的資訊
⚠️ 這裡建議鹽最好亂寫 寫得越亂越好,各種字元都加上,並亂序寫。
加密
我們在使用者登入成功的時候將使用者資訊加密。所以我在我的管理員登入介面那寫。
我的在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(就是所謂的同一個賬戶在多地登入會擠掉其他人的登入狀態)。
解密
加密完之後使用者端請求必然需要帶上登入態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控制介面許可權