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

重要說明

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

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

主要是 安裝一些東西node

  1. 項目的初始化

以下是咱們的項目文件夾結構git

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

  1. 項目的包管理

首先咱們須要使用express@next框架,由於只用next才能在裏面使用async es7的一些東西,
咱們還須要mongoose來操做數據庫
咱們還須要bcrypt對數據庫裏面的密碼進行加密
咱們還須要jsonwebtoken快捷的生成tokenmongodb

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

好了以上就是咱們須要作的shell

設計路由邏輯

首先咱們須要在這裏設計幾個接口,他們是,而且完成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請求管理的頁的接口就須要驗證express

/router/index.jsnpm

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的加密,你能夠經過引入文件去配置你的加密信息
相關文章
相關標籤/搜索