Node教程——封裝一個token驗證器


藍圖blueprint,git地址:https://github.com/BM-laoli/Node-token-forApi-blueprint

重要說明

這個輪子是 使用 express@5.0 + MongoDB構建起來的一個 node后台通用的驗證器,里面主要講的就是使用jwt,token進行驗證,當然你想使用session也沒問題,但是這個藍圖工程只包含了token字段內容

首先是初始化我們的項目,

主要是 安裝一些東西

  1. 項目的初始化

如下是我們的項目文件夾結構

![img](file:///C:\Users\ADMINI~1\AppData\Local\Temp\1589190039(1).jpg)

  1. 項目的包管理

首先我們需要使用express@next框架,因為只用next才能在里面使用async es7的一些東西,
我們還需要mongoose來操作數據庫
我們還需要bcrypt對數據庫里面的密碼進行加密
我們還需要jsonwebtoken快捷的生成token

npm install express@next
npm install mongoose
npm install bcrypt
npm install jsonwebtoken

好了以上就是我們需要做的

設計路由邏輯

首先我們需要在這里設計幾個接口,他們是,並且完成post請求的配置解析json

  • 測試接口
  • 注冊接口
  • 登錄接口
  • 獲取所有用戶信息接口
  • 等錄之后的權限校驗接口
  1. 構建入口,並且完成json的解析
    /app.js

const express = require('express');

const app = express();


//解析一遍post參數
app.use(express.urlencoded({ extended: true }))
app.use(express.json())


//路由分發器
app.get('/test', async(req, res) => {
    res.send('測試打通!沒問題')
})


app.use('/api', require('./route/index'))


app.listen(3001, async() => {
    console.log('http://localhost:3001');
})
  1. 構建分路由

注意這里只是簡單的做一些功能測試,后續我們會把這個東西都刪掉,把驗證丟給router去驗證,目的就是模塊化處理業務,請求主頁不需要token請求管理的頁的接口就需要驗證

/router/index.js

const express = require('express');

const indexApi = express.Router()


indexApi.post('/register', async(req, res) => {
    console.log(req.body);
    res.send('register!!ok')
})


indexApi.post('/login', async(req, res) => {
    console.log(req.body);
    res.send('login!!ok')
})


indexApi.get('/users', async(req, res) => {
    res.send('users!!ok')
})


indexApi.get('/profile', async(req, res) => {
    res.send('profile!!ok')
})




  1. 完成對應的接口測試
    /.http

@uri = http://localhost:3001/api


### 測試
GET {{uri}}

### 展示出所有的用戶
GET  {{uri}}/users



###  注冊
POST {{uri}}/register
Content-Type: application/json

{
    "username":"user2",
    "password":"123456"
}

### 登錄
POST {{uri}}/login
Content-Type: application/json

{
    "username":"user2",
    "password":"123456"
}


### 獲取個人信息,傳遞的是當前保持狀態了的用戶
GET {{uri}}/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlYjhhNDIzOTM4YzdhNmQ3NDg5ZDJlMyIsImlhdCI6MTU4OTE2MDY0Nn0.UeSGbDgUrQaThemD18iIAGW6t-lc8R_R5tDvFamrgDw


設計實現數據庫Model

在這里我們需要完成的工作有:

  • 使用mongoose連上數據庫
  • 創建shcema規則
  • 使用規則創建集合
  • 倒出集合操作對象,創建集合交給理由或者中間件去做,這里功能比較簡單我們可以直接丟給路由去做,但是為了保持編程的風格一致,我打算丟到中間件里面去

我們把model都寫在一個文件里有點不太妥當,當然這樣做是完全沒有問題的,如果項目有良好的架構我們可以后期考慮把他們分類的去構建model,比如與用戶相關的molde都放在一個文件里,等等

/model/model.js

const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

mongoose.connect('mongodb://localhost:27017/express-auth', { useUnifiedTopology: true, useNewUrlParser: true, useCreateIndex: true });


const UserSchma = new mongoose.Schema({

    username: {
        type: String,
        unique: true //只需要usernam為唯一值
    },

    password: {
        type: String,
    }

})

const User = mongoose.model('User', UserSchma) //雖然這個表的名字是User但是實際上數據庫創建的時候會給你變成users

// 倒出數據操作對象
module.exports = { User }

注意啊,我們只是定義的集合還有數據庫的表(集合)操作對象,還米有拿去業務中使用,接下里的章節我們會拿到具體的業務里去使用
好了我們階段性的回顧一下我們現在的文件夾里面都有哪些東西吧

實現新增(實際上就是注冊)用戶接口

這里我們定義就能實現我們的業務功能了,首先要說明的是,我們這里依然使用標准化的項目開放方式,把功能寫在中間件里,

這里的定義的中間件處理一個專門的業務就是User相關的業務,這是我工作中編寫nodejs的全棧項目的一個習慣
/middleware/users.js

const { User } = require('../model/model')

module.exports = {

    register: async(req, res, next) => {
        // console.log(req.body);
        let { username, password } = req.body

        const user = await User.create({
            username: username,
            password: password
        })
        req.user = user

        next()
    }
}

在路由里面你只需要弄這個就好了。實際上中間件就是一個對象

const users = require('../middleware/users')
+++
indexApi.post('/register', users.register, (req, res) => {
    res.send(req.user)
})

+++

實現展示用戶功能

前面我們實現了用戶的注冊新增功能,那么我們就來實現一些查看所有用戶功能。
用了前面的架構模式,我們的這個業務就可以全部寫在middelwear里了,如果有設計賦值的操作。我們還可以創建一個工具middlewear,來幫助我們實現復雜的功能,這就是模塊化開發

/middleware/users.js

+++
 //查看所有用戶
    showUser: async(req, res, next) => {

        //為什么是find就可以了,因為mongoose給你封裝了
        const user = await User.find();

        req.user = user;
        next();

    }
+++


/router/index.js

const users = require('../middleware/users')
+++
indexApi.get('/users', users.showUser, async(req, res) => {
    res.send(req.user)
})
+++

實現bcrypt加密密碼

實際上實現bcrypt的加密非常的簡單,只需要調用方法就好了

+++
  password: {
        type: String,
        set(val) { //val是自定義的保存前的加密,返回的值就是加密之后的密碼
            return require('bcrypt').hashSync(val, 10) //進行散列之后的密碼,10就是加密強度
        }
    }
+++

加密之后的密碼驗證怎么做?實際上這里就是登錄功能

實際上也非常的簡單,先驗證用戶名是否正確 如果正確就去根據用戶名查用戶數據,然后拿到加密之后的密碼,然后使用bcrypt自己的驗證方式去驗證就好了

/middleware/users.js

+++
   //登錄器
    login: async(req, res, next) => {

        let { username, password } = req.body

        const user = await User.findOne({
            username: username
        })

        //驗證用戶名
        if (!user) {
            return res.status(422).send({ message: '用戶名不存在' })
        }
        const isPasswordValid = bcrypt.compareSync(password,
            user.password
        )

        //驗證密碼
        if (!isPasswordValid) {
            return res.status(422).send({ message: '密碼無效' })
        }
        req.user = user
        next()
    }
+++

/router/index.js

indexApi.post('/login', users.login, async(req, res) => {
    res.send(req.user)
})



實現token的下發

實際上這個也非常的簡單,我們只需要在登錄成功的時候給用戶加上一個token就好了

這里的業務邏輯核心,其實就是這個token該如何加

  • 修改一下我們的登錄功能,使得用戶登錄的時候加上一個用以驗證用戶登錄狀態的token
    /middleware/users.js
+++
 const jwt = require('jsonwebtoken')
+++

+++
 //登錄器
    login: async(req, res, next) => {
    +++

        //生成token,jwtToken   
        //生成簽名,我們給id丟進去據好了
    const token = jwt.sign({
            //加密的簽名
            id: String(user._id),
            //密鑰
        }, 'asdasdasdasdasdasdasdasdasdasdasd') //這個東西實際上是一串秘鑰,用來對應每一個的tonken驗證器,它應該被寫一個單獨的文件里
    res.user = {
        user,
        token

    }
    +++
}
  • 我們看一些測試的結果
{
  "user": {
    "_id": "5eb933c9cf3c3f33fcadb560",
    "username": "user2",
    "password": "$2b$10$n2OHQzuSuUtwWpg.YuiDO.FPM4Q9nrBdqANLB3Wkh67P.MonpIyYi",
    "__v": 0
  },
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlYjkzM2M5Y2YzYzNmMzNmY2FkYjU2MCIsImlhdCI6MTU4OTE5Nzc4MX0.a4vrQwTeGsuI320m1OYsjSB8abdxxm8TReKYg6UKbVQ"
}

實現用戶的token校驗.

這里我們把auth做成一個中間件,這個中間件可以加載需要驗證的路由的前面,如果通過驗證就放行,要不然就放行next,這就是驗證器auth的實現原理,非常的簡單
/middleware/auth.js


const jwt = require('jsonwebtoken')
const { User } = require('../model/model')
module.exports = {
    auth: async(req, res, next) => {
        //注意啊這個字段是我們前端需要實現的,因為這是后台要求的
        let raw = String(req.headers.authorization).split(' ').pop() //我為啥要用空格分隔,因為我發起請求的時候多加了一個字段,

        const tokenData = jwt.verify(raw, 'asdasdasdasdasdasdasdasdasdasdasd')
        let { id } = tokenData

        
        //加到req上以便以給下一個中間件使用
        req.user = await User.findById(id)
        
        next()

    }
}

假設我們現在需要把這個auth用於我們的 profile接口做驗證,那么我們可以這樣來使用

//核心token驗證器
indexApi.get('/profile', auth.auth, async(req, res) => {
    res.send(req.user)
})

注意,以上的所有都只是一個小小的demo。正式的打包再我這里

整理好所有的目錄,打包構建成藍圖並且發布上git

  • 我為什么要整理成藍圖?因為我希望我的token驗證其能復用到很多地方去,假設我以后的項目需要用這個那么我就直接下載藍圖,這樣我的token就不用我再去啰嗦的寫了,這也實際上已經是一個初步的node框架的雛形了。

我把它寫在了blueprint_for_token_v3中。你可以直接git clone去使用它構建你的node項目

優化項目結構打包,我做了那些事?(主要就是以下愛的事情)

  1. 優化目錄結構
  2. 整理接口 去掉了沒有用的接口,只保留了一些基礎的接口
  3. 使用說明:你只需要npm install 就能實現token的驗證了,需要驗證之后的接口請在admin之后的路由書寫,當然你可以自定義路由,拿着我的auth去做驗證就可以了
  4. 建議你使用非對稱加密的密鑰對,進行token的加密,你可以通過引入文件去配置你的加密信息


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM