0.效果演示
插入視頻插不進來,就很煩,可以出門右拐去優酷看下(點我!),
1.后端搭建
1.1專案結構
首先看一下后端的server目錄

挨個解釋一下
- 首先dbs檔案夾顧名思義,操作資料庫的,modules就是操作資料庫的mongoose模型,
- config.js是為了方便修改資料庫資料,
- interface就是介面檔案夾,utils就是工具的意思唄,介面需要用到的axios和賬號集權的passport都在這里修改(passport是啥待會兒再細說),
- 和utils同級的就是users.js 就是user介面的路由,具體的邏輯就在這個檔案里
- index.js和dbs,interface檔案夾同級,是整個server目錄的入口檔案,
1.2后端配置
首先看一下config.js,直接放代碼,
1 // 匯出相應的配置,然后可以方便的用下面的資料,改的話也比較方便修改, 2 export default { 3 //dbs 表示需要連接的服務器 4 dbs: "mongodb://127.0.0.1:27017/myblog2", 5 //redis物件是提供redis的資訊 6 redis: { 7 get host() { 8 return "127.0.0.1"; 9 }, 10 get port() { 11 return 6379; 12 } 13 }, 14 // smtp物件是利用郵箱來發送驗證碼的 15 smtp: { 16 get host() { 17 return "stmp.qq.com"; 18 }, 19 get user() { 20 return "[email protected]"; 21 }, 22 // 這個pass碼是qq郵箱給提供的,下面是隨機打的不是真實的, 23 get pass() { 24 return "pfpeqwddadadasdaf"; 25 }, 26 // 制造一個隨機的驗證碼 27 get code() { 28 return () => { 29 return Math.random() 30 .toString(16) 31 .slice(2, 6) 32 .toUpperCase(); 33 }; 34 }, 35 // 創建一個時間,時間就是發送郵箱的時間, 36 get expire() { 37 return () => { 38 return new Date().getTime() + 60 * 60 * 1000; 39 }; 40 } 41 } 42 };
針對于smtp協議,就是你可以利用它來發送驗證碼的,至于如何獲取qq郵箱的pass碼?
↓首先打開郵箱點擊設定:

↓點擊上方navbar的賬號按鈕

↓滑到下面,找到smtp選項

1.3創建資料模型
鋪墊性的東西太多,建議都學完再來看,
1、mongodb和mongoose不詳細展開講了,mongodb可參考我之前寫的入門教程(點我!)
2、mongoose大體流程是引入,創建shcema ,創建model,然后進行操作,詳情進入官網(點我!),針對于這個順序先不引入,引入是index.js里的,現在先講創建schema和創建model這個部分的,
3、用了些async await和解構賦值的語法,如果有不太理解的可以自動跳轉去學習一下新版本的js,推薦阮一峰的《ECMAScript 6 入門教程》(直接點書名!)
不多說,直接放代碼:
1 // server/dbs/modules/user.js 2 // 匯入Monogoose 3 import mongoose from "mongoose"; 4 // 創建schema 5 const Schema = mongoose.Schema; 6 // 創建userSchema 7 const UserSchema = new Schema({ 8 username: { 9 type: String, 10 unique: true, 11 require: true 12 }, 13 password: { 14 type: String, 15 require: true 16 }, 17 email: { 18 type: String, 19 require: true 20 } 21 }); 22 // 匯出user模型 23 export default mongoose.model("User", UserSchema);
現在登錄注冊需要存入庫中的只需要這些,驗證方面的快取資料統一存到redis里,reids的邏輯統一在users的介面里講,
1.4創建users介面
可能大家沒注意到我沒說utils里的內容,axios和passport.js ,因為現在時機未到,
現在先配置一下user介面,支起來個架子,然后再談邏輯
// koa-router必引的,不多解釋 import Router from "koa-router"; // 發送驗證碼用redis,因為可能需求量會很大,redis效率較高 import Redis from "koa-redis"; // 用nodeMailer插件來發送郵件, import nodeMailer from "nodemailer"; // USer模型,來操作mongodb import User from "../dbs/modules/users"; // 用來發郵件的配置引數 import Email from "../dbs/config"; // axios來請求資料 import axios from "./utils/axios"; //來引入passprot中間件 import Passport from "./utils/passport"; // 創建一個路由,他的最開始用/users let router = new Router({ prefix: "/users" }); // 創建一個redis的倉庫, let Store = new Redis().client; // 匯出router export default router;
redis的邏輯是引入koa-redis插件,然后新建store物件,然后我們針對store物件進行相應的操作,詳細介紹請去npm看(點我!)
1.4.1 注冊驗證碼介面
首先第一個任務那就是注冊,注冊的邏輯是填寫郵箱和用戶名,確定密碼,然后發送驗證碼郵件進行驗證,
那么首先配置的就是發送驗證碼的路由,因為都是線性的代碼,所以直接放代碼,代碼如下:
1 // 發送驗證碼 2 router.post("/verify", async ctx => { 3 //獲取username 4 let username = ctx.request.body.username; 5 6 //可以不看6-16行,看到結尾再回來看, 7 //獲得驗證碼的有效時間 8 const saveExpire = await Store.hget(`nodemail:${username}`, "expire"); 9 //如果驗證碼的有效時間太短,就不能再發次發送, 10 if (saveExpire && new Date().getTime() - saveExpire < 0) { 11 ctx.body = { 12 code: -1, 13 msg: "驗證請求過于頻繁,1分鐘內1次" 14 }; 15 return false; 16 } 17 //然后用nodeMailer創建一個transport 18 let transporter = nodeMailer.createTransport({ 19 // server名稱 20 service: "qq", 21 // user的名稱和他的pass 22 auth: { 23 user: Email.smtp.user, 24 pass: Email.smtp.pass 25 } 26 }); 27 //獲取到驗證碼和時間,還有用戶輸入的郵箱和用戶名 28 let ko = { 29 code: Email.smtp.code(), 30 expire: Email.smtp.expire(), 31 email: ctx.request.body.email, 32 user: ctx.request.body.username 33 }; 34 // 郵件的組態檔 35 let mailOptions = { 36 from: `" 博客注冊認證郵件" <${Email.smtp.user}>`, 37 to: ko.email, 38 subject: "王梓瑞的博客注冊驗證碼", 39 html: `驗證碼是${ko.code},請盡快完成注冊!` 40 }; 41 await transporter.sendMail(mailOptions, (error, info) => { 42 if (error) { 43 return console.log(error); 44 } else { 45 // 當郵件發送成功了,就將資料保存起來,以后可以拿來用, 46 Store.hmset( 47 `nodemail:${ko.user}`, 48 "code", 49 ko.code, 50 "expire", 51 ko.expire, 52 "email", 53 ko.email 54 ); 55 } 56 }); 57 ctx.body = { 58 code: 0, 59 msg: "驗證碼已發送,可能會有延時,有效期1分鐘" 60 }; 61 });
1.4.2注冊介面
等發完驗證碼 ,我們就可以繼續進行注冊操作,
同樣直接放代碼,很好理解,
router.post("/signup", async ctx => {
// 先獲取表單里的資訊,這么寫是es6的解構賦值語法
const { username, password, email, code } = ctx.request.body;
if (code) {
const saveCode = Store.hget(`nodemail:${username}`, "code");
const saveExpire = Store.hget(`nodemail:${username}`, "expire");
if (saveCode == code) {
if (new Date().getTime() - saveExpire > 0) {
ctx.body = {
code: -1,
msg: "驗證碼已過期,請重新嘗試"
};
return false;
}
} else {
ctx.body = {
code: -1,
msg: "請填寫正確的驗證碼"
};
}
} else {
ctx.body = {
code: -1,
msg: "未輸入驗證碼"
};
}
let user = await User.find({ username });
if (user.length) {
ctx.body = {
code: -1,
msg: "已被注冊"
};
return;
}
let nuser = await User.create({ username, password, email });
console.log(nuser);
if (nuser) {
ctx.body = {
code: 0,
msg: "注冊成功"
};
} else {
ctx.body = {
code: -1,
msg: "注冊失敗"
};
}
});
1.4.3 登錄介面
進行到這里就涉及到了passport,因為登陸的狀態是需要集中去進行管理的,那么就涉及到Passport這個插件,如果需要快速上手的話,可以看看這篇簡書(點我!)
我這里直接就是講實戰了,不多說了,可以參考著上面的簡書加上我的代碼自行理解,
1 // server/interface/utils/passport.js 2 // 引入 passprot ,然后引入本地策略,就是驗證用戶是否成立,最后引入操作模型, 3 import passport from "koa-passport"; 4 import LocalStrategy from "passport-local"; 5 import UserModel from "../../dbs/modules/users"; 6 7 // passport 加載策略中間件,然后通過新建location物件在里面進行對用戶的鑒定, 8 passport.use( 9 //創建新的策略,然后三個引數分別是 用戶名密碼和回呼 10 new LocalStrategy(async function(username, password, done) { 11 //此處用where是表示搜索的時候引數是一個物件 12 let where = { 13 username 14 }; 15 // 用user的Mongoose的模型來搜索user在資料庫中的記錄,用res來接收 16 const res = await UserModel.findOne(where); 17 // 判斷res是否存在, 不存在就用策略的回呼函式done回傳一個用戶不存在的錯誤資訊, 18 if (res != null) { 19 // 如果資料庫里的Password和輸入的password吻合,就回傳一個res 20 if (res.password === password) { 21 return done(null, res); 22 } else { 23 // 不吻合就回傳一個密碼錯誤, 24 return done(null, false, "密碼錯誤"); 25 } 26 } else { 27 return done(null, false, "用戶不存在"); 28 } 29 }) 30 ); 31 32 // 序列化和反序列化,沒什么大事, 33 passport.serializeUser(function(user, done) { 34 done(null, user); 35 }); 36 37 passport.deserializeUser(function(user, done) { 38 return done(null, user); 39 }); 40 41 // 匯出passport集權控制中間件, 42 export default passport;
再放user.js介面里的代碼
1 router.post("/signin", async (ctx, next) => { 2 return Passport.authenticate("local", (err, user, info, status) => { 3 if (err) { 4 ctx.body = { 5 code: -1, 6 msg: err 7 }; 8 } else { 9 if (user) { 10 ctx.body = { 11 code: 0, 12 msg: "登錄成功", 13 user 14 }; 15 return ctx.login(user); 16 } else { 17 ctx.body = { 18 code: 1, 19 msg: info 20 }; 21 } 22 } 23 })(ctx, next); 24 });
這里可能有人會有疑問,就說我的介面都沒有獲取到ctx里面的username和password資料,怎么直接就return了passport的物件呢?
問題就在必須在index.js讓koa2物件使用bodyParser的中間件,代碼如下:
// bodyParser中的extendTypes必須要加,要不然passport就無法決議username和passport app.use( bodyParser({ extendTypes: ["json", "form", "text"] }) );
然后就可以決議到登錄過來的username和passport了,
1.4.4 退出和查詢用戶介面
直接放代碼,沒有難度
1 router.get("/getUser", async ctx => { 2 if (ctx.isAuthenticated()) { 3 const { username, email } = ctx.session.passport.user; 4 ctx.body = { 5 user: username, 6 email 7 }; 8 } else { 9 ctx.body = { 10 user: "", 11 email: "" 12 }; 13 } 14 });
退出登錄
1 router.get("/exit", async (ctx, next) => { 2 await ctx.logout(); 3 if (!ctx.isAuthenticated()) { 4 ctx.body = { 5 code: 0 6 }; 7 } else { 8 ctx.body = { 9 code: -1 10 }; 11 } 12 });
如果你想獲取用戶資料的話還有第二種方法,從session獲取,session是什么?大家自行百度下,簡單來說就是服務端的cookie,
然后運用vuex 配合 nuxt.js的nuxtServerInit方法,這個又需要理解vuex和nuxtServerInit,這個放上鏈接,vuex,nuxtServerInit
專案結構

如果用vue-cli會有modules檔案夾來放模型的,但是nuxt.js直接都放在平級了,詳情可參照nuxt.js檔案,(點我!)
同樣直接放代碼吧,
1 //store/user.js 2 const state = () => ({ 3 user: "" 4 }); 5 const mutations = { 6 setUser(state, param) { 7 state.user = param; 8 } 9 }; 10 const actions = { 11 setUser: ({ commit }, param) => { 12 commit("setUser", param); 13 } 14 }; 15 16 export default { state, mutations, actions };
1 //store/index.js 2 const actions = { 3 async nuxtServerInit({ commit }, { req }) { 4 if (req.user) { 5 commit("user/setUser", req.user.username); 6 } 7 } 8 }; 9 10 export { actions };
然后我們就可以在頁面的任何位置呼叫 $store.state.user.user 即可獲得用戶的用戶名了,可以省去異步獲取的操作,
1.5 index.js檔案
直接放代碼,之前解釋的都解釋過了,這個就是一個啟動后端的檔案
1 // server/index.js 2 const Koa = require("koa"); 3 const consola = require("consola"); 4 const { Nuxt, Builder } = require("nuxt"); 5 6 import bodyParser from "koa-bodyparser"; // 這個一開始就要加,不加的話決議不出來request.body,post請求就白給, 7 import json from "koa-json"; 8 import mongoose from "mongoose"; 9 import dbConfig from "./dbs/config"; 10 import Redis from "koa-redis"; 11 import session from "koa-generic-session"; 12 import users from "./interface/users"; 13 import passport from "./interface/utils/passport"; 14 15 const app = new Koa(); 16 17 // Import and Set Nuxt.js options 18 const config = require("../nuxt.config.js"); 19 config.dev = app.env !== "production"; 20 21 async function start() { 22 // Instantiate nuxt.js 23 const nuxt = new Nuxt(config); 24 25 const { 26 host = process.env.HOST || "127.0.0.1", 27 port = process.env.PORT || 3000 28 } = nuxt.options.server; 29 30 //這個是加密用的 31 app.keys = ["my", "keyskeys"]; 32 //是否設定代理 33 app.proxy = true; 34 //session的前綴 35 app.use(session({ key: "my", prefix: "my:uid", store: new Redis() })); 36 //mongoose鏈接Mongodb 37 mongoose.connect(dbConfig.dbs, { 38 useNewUrlParser: true 39 }); 40 //初始化passport 41 app.use(passport.initialize()); 42 //讓passport使用session 43 app.use(passport.session()); 44 45 // Build in development 46 if (config.dev) { 47 const builder = new Builder(nuxt); 48 await builder.build(); 49 } else { 50 await nuxt.ready(); 51 } 52 //決議json用的中間件 53 app.use(json()); 54 // bodyParser中的extendTypes必須要加,要不然passport就無法決議username和passport 55 app.use( 56 bodyParser({ 57 extendTypes: ["json", "form", "text"] 58 }) 59 ); 60 // 加載路由中間件 61 app.use(users.routes()).use(users.allowedMethods()); 62 63 app.use(ctx => { 64 ctx.status = 200; 65 ctx.respond = false; // Bypass Koa's built-in response handling 66 ctx.req.ctx = ctx; // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash 67 nuxt.render(ctx.req, ctx.res); 68 }); 69 70 app.listen(port, host); 71 consola.ready({ 72 message: `Server listening on http://${host}:${port}`, 73 badge: true 74 }); 75 } 76 77 start();
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/154864.html
標籤:JavaScript
上一篇:一起學Vue之事件處理
