• 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();
  • 相关阅读:
    TCP/IP研究(1)-Timer(2)
    linux学习
    TCP/IP研究(2)-TCB
    vi学习笔记
    TCP/IP研究(1)-Timer
    yxr:Makefile 简单样本
    zt:vim环境配置
    zt:文件轻松比对,伟大而自由的比较软件们
    就是这么简单!使用Rest-assured 测试Restful Web Services
    手把手教你接口自动化测试 – SoapUI & Groovy
  • 原文地址:https://www.cnblogs.com/wangzirui98/p/12209082.html
Copyright © 2020-2023  润新知