vue koa2 mongodb 從零開始作我的博客(二) 登陸註冊功能後端部分

0.效果演示

插入視頻插不進來,就很煩。能夠出門右拐去優酷看下(點我!)。 html

1.後端搭建

1.1項目結構

首先看一下後端的server目錄vue

 挨個解釋一下node

  • 首先dbs文件夾顧名思義,操做數據庫的,modules就是操做數據庫的mongoose模型。
  • config.js是爲了方便修改數據庫數據。
  • interface就是接口文件夾,utils就是工具的意思唄,接口須要用到的axios和帳號集權的passport都在這裏修改(passport是啥待會兒再細說)。
  • 和utils同級的就是users.js 就是user接口的路由,具體的邏輯就在這個文件裏
  • index.js和dbs,interface文件夾同級。是整個server目錄的入口文件。

 

1.2後端配置

首先看一下config.js,直接放代碼。ios

 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 "470557449@qq.com";
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碼?es6

↓首先打開郵箱點擊設置:redis

↓點擊上方navbar的帳號按鈕mongodb

↓滑到下面,找到smtp選項 vuex

 

1.3建立數據模型

鋪墊性的東西太多,建議都學完再來看。vue-cli

一、mongodb和mongoose不詳細展開講了,mongodb可參考我以前寫的入門教程(點我!數據庫

二、mongoose大致流程是引入,建立shcema ,建立model。而後進行操做。詳情進入官網(點我!)。針對於這個順序先不引入,引入是index.js裏的,如今先講建立schema和建立model這個部分的。

三、用了些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,這個放上連接,vuexnuxtServerInit

項目結構

 

 

若是用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();
相關文章
相關標籤/搜索