重要說明
這個輪子是 使用 express@5.0 + MongoDB構建起來的一個 node後臺通用的驗證器,裡面主要講的就是使用jwt,token進行驗證,當然你想使用session也沒問題,但是這個藍圖工程只包含了token欄位內容
首先是初始化我們的專案,
主要是 安裝一些東西
- 專案的初始化
如下是我們的專案資料夾結構
![img](file:///C:\Users\ADMINI~1\AppData\Local\Temp\1589190039(1).jpg)
- 專案的包管理
首先我們需要使用express@next框架,因為只用next才能在裡面使用async es7的一些東西,
我們還需要mongoose來運算元據庫
我們還需要bcrypt對資料庫裡面的密碼進行加密
我們還需要jsonwebtoken快捷的生成token
npm install express@next
npm install mongoose
npm install bcrypt
npm install jsonwebtoken
好了以上就是我們需要做的
設計路由邏輯
首先我們需要在這裡設計幾個介面,他們是,並且完成post請求的配置解析json
- 測試介面
- 註冊介面
- 登入介面
- 獲取所有使用者資訊介面
- 等錄之後的許可權校驗介面
- 構建入口,並且完成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');
})
- 構建分路由
注意這裡只是簡單的做一些功能測試,後續我們會把這個東西都刪掉,把驗證丟給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')
})
- 完成對應的介面測試
/.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專案
優化專案結構打包,我做了那些事?(主要就是以下愛的事情)
- 優化目錄結構
- 整理介面 去掉了沒有用的介面,只保留了一些基礎的介面
- 使用說明:你只需要npm install 就能實現token的驗證了,需要驗證之後的介面請在admin之後的路由書寫,當然你可以自定義路由,拿著我的auth去做驗證就可以了
- 建議你使用非對稱加密的金鑰對,進行token的加密,你可以通過引入檔案去配置你的加密資訊