涉及依賴:mongoose bcrypt jsonwebtoken http-assert express
整體目錄:
|--項目
····|--server
····|--admin
登錄邏輯:
-
1.服務(wù)端:先建表,存儲用戶名和密碼,存的是加密后的密碼:server-->models-->AdminUser.js
const mongoose = require("mongoose"); const schema = new mongoose.Schema({ username: { type: String }, password: { type: String, select:false, //查表時不帶出password字段 set(val) { // 使用bcrypt進(jìn)行散列保存 12代表加密程度 return require('bcrypt').hashSync(val,12); } } }); module.exports = mongoose.model("AdminUser", schema);
-
2.前端:編寫登錄頁畦徘,涉及到一個接口:admin-->src-->Login.vue
async login(){ const res = await this.$http.post('login',this.model) localStorage.token = res.data.token; this.$router.push('/') this.$message({ type:'success', message:'登錄成功' }) }
這里面寫的請求地址是
login
是因為已經(jīng)在http.js
中定義了baseURL
:admin-->src-->http.jsimport axios from "axios"; import Vue from "vue"; import router from "./router" const http = axios.create({ baseURL: "http://localhost:3000/admin/api" });
-
3.服務(wù)端:添加登錄頁接口:server-->routes-->admin-->index.js
module.exports = app =>{ // 生成token的插件 // 文檔:https://www.npmjs.com/package/jsonwebtoken const jwt = require("jsonwebtoken"); // http錯誤處理插件 const assert = require("http-assert"); const AdminUser = require("../../models/AdminUser"); app.post("/admin/api/login", async (req, res) => { // 0.將所有請求的內(nèi)容賦值給username和password,需要做app.use(express.json())處理 const { username, password } = req.body; // 1.根據(jù)用戶名找用戶 +password是因為查表默認(rèn)不帶出password字段脐供,而這里需要 const user = await AdminUser.findOne({ username }).select("+password"); assert(user, 422, "用戶不存在"); // 2.校驗密碼 const isValid = require("bcrypt").compareSync(password, user.password); assert(isValid, 422, "密碼錯誤"); // 3.返回token,使用user._id和預(yù)先定義的字符串生成token const token = jwt.sign({ id: user._id }, app.get("secret")); res.send({ token }); }); app.use(async (err, req, res, next) => { res.status(err.statusCode || 500).send({ message: err.message }); }); }
由于上面用到了
app.get("secret")
(這里需要注意的是express多參數(shù)使用時.get的功能)借跪,所以需要先設(shè)置政己,在server-->index.js中:const express = require('express') const app = express() app.use(require('cors')()) // 使用跨域模塊 app.use(express.json()) // 將前端給后端的數(shù)據(jù)轉(zhuǎn)成json格式,否則沒有req.body app.set('secret','fdfdashjkhu') // 這里設(shè)置的字符串可以是任意的 require('./plugins/db')(app) require('./routes/admin')(app) app.listen(3000,()=>{ console.log('http://localhost:3000'); })
到這里就已經(jīng)做好了登錄模塊的內(nèi)容:用戶校驗掏愁,密碼校驗歇由,生成token卵牍,接下來將每個接口使用token作為請求頭,使得只有在token校驗通過之后才能請求其他接口
-
4.服務(wù)端:添加中間件server-->routes-->admin-->index.js
const authMiddleware = async (req, res, next) => { const token = String(req.headers.authorization || "").split(" ").pop(); assert(token, 401, "請?zhí)峁﹋wt token"); const { id } = jwt.verify(token, app.get("secret")); assert(id, 401, "無效的jwt token"); req.user = await AdminUser.findById(id); assert(req.user, 401, "請先登錄"); await next(); };
然后在所有請求的中間添加:
app.use( "/admin/api/rest/:resource", authMiddleware, async (req, res, next) => { // inflection是一個類似babel的模塊印蓖,.classify可以將復(fù)數(shù)單詞轉(zhuǎn)為單數(shù) //詳見https://www.helplib.com/GitHub/article_94504 const modelName = require("inflection").classify(req.params.resource); req.model = require(`../../models/${modelName}`); next(); }, router );
-
5.前端添加請求頭admin-->src-->http.js
http.interceptors.request.use( function(config) { // 如果有token 就設(shè)置 Authorization localStorage.token && (config.headers.Authorization = "Bearer " + localStorage.token || ""); return config; }, function(error) { return Promise.reject(error); } );
攔截所有錯誤:
http.interceptors.response.use( res => { return res; }, err => { console.log(err.response.data); if (err.response.data.message) { Vue.prototype.$message({ type: "error", message: err.response.data.message }); if (err.response.status == 401) { router.push('/login') } } return Promise.reject(err); } );
至此接口驗證token的邏輯也寫好了辽慕。
對于一名前端來說,做完上面5步就基本沒什么復(fù)雜內(nèi)容了赦肃,后面還差用戶注冊步驟,但是上面的步驟掌握了注冊也不是什么難事了公浪。
參考文檔: