vue koa2 mongodb 从零开始做个人博客(二) 登录注册功能后端部分

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 "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码?

↓首先打开邮箱点击设置:

↓点击上方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,这个放上链接,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();
posted @ 2020-01-19 00:04  wang999  阅读(615)  评论(0编辑  收藏  举报