• 从零开始,搭建一个简单的购物平台(二)


    从零开始,搭建一个简单的购物平台(一):https://blog.csdn.net/time_____/article/details/105191286
    项目源码(持续更新):
    https://gitee.com/DieHunter/myCode/tree/master/shopping

    上一篇文章将项目主体以及插件模块搭建完成,这篇文章目的主要搭建服务端token验证及登录功能

    服务端:

    文件目录结构

    入口文件为server.js,简单配置一下,先让他能运行起来

    const express = require("express");
    const app = express();
    
    app.listen(1024, () => {
      console.log("Server Start~");
    });

    使用终端cd到server.js目录,输入node server运行入口文件,显示Server Start~表示运行成功,之后引入一些模块

    const express = require("express");
    const app = express();
    const routes = require("./routes/routes");
    const cors = require("cors"); //引入cors模块(解决跨域问题)
    const path = require("path");
    app.use(cors());
    app.all("*", function (req, res, next) {
      //设置允许跨域的域名,*代表允许任意域名跨域
      res.header("Access-Control-Allow-Origin", "*");
      res.header("Access-Control-Allow-Headers", "content-type"); //允许的header类型
      res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); //跨域允许的请求方式
      next(); //是否继续向下执行
    });
    let bodyParser = require("body-parser"); //post传输数据类型
    app.use(
      bodyParser.urlencoded({
        extended: false,
      })
    );
    new routes(app);//初始化路由
    app.use(bodyParser.json());
    app.use("/public", express.static(path.join(__dirname, "./public")));//静态目录
    
    app.listen(1024, () => {
      console.log("Server Start~");
    });
    

    全部设置完之后进行下一步,配置路由与token验证

    在utils.js文件中新建一个静态类,并且引入jwt(jsonwebtoken),在config.js文件中新建config静态类用于存放配置变量(接口名,公钥秘钥,常量),此外还需用到crypto进行数据加解密,Bcrypt密码盐加密

    utils.js

    const jwt = require("jsonwebtoken");
    const config = require("../config/config");
    const cryptoJS = require("crypto-js");//用来加密解密前端参数
    let { UserKey, AdminKey,CryptoKey} = config;//token加密关键字,秘钥
    let key = cryptoJS.enc.Utf8.parse(CryptoKey);//生成16进制秘钥
    module.exports = class Utils {
       static parseUrl(req, res) {//获取前端传递的参数
        return req.method == "POST" ? req.body : this.urlSplit(req.url);
      }
       static urlSplit(url) {//get获取的参数解析
        let list = url.split("?")[1].split("&");
        let leng = list.length;
        let obj = {};
        for (let i = 0; i < leng; i++) {
          let key = list[i].split("=")[0];
          let val = list[i].split("=")[1];
          obj[key] = val;
        }
        return obj;
      }
    /*
       * @param {string} type 'user'||'admin'      用户类型
       * @param {string} user                      用户名  
       * @param {bool} rempsd                      是否记住密码
       */
      static createToken = (type, user, rempsd) => {//生成token,用户登录时调用
        let payload = {
          user: user,
        };
        return jwt.sign(payload, type == "admin" ? AdminKey : UserKey, {
          expiresIn: rempsd ? "3d" : "6h",
        });
      };
     /*
       * @param {object} req       前端请求对象
       * @param {object} res       服务端接收对象
       * @param {fn} next          中间件响应方法
       */
      static checkToken = (req, res, next) => {
        let _data = this.parseUrl(req, res); //解析前端参数
        if (_data.crypto) {
          _data = this.getCrypto(_data.crypto); //对前端参数解密
        }
        let isUser = true; //用户
        let isAdmin = true; //管理员
        let _decoded = ""; //加密的用户名
        jwt.verify(_data.token, UserKey, function (err, decoded) {
          if (err) {
            isUser = false;
          } else {
            _decoded = decoded;
          }
        });
        jwt.verify(_data.token, AdminKey, function (err, decoded) {
          if (err) {
            isAdmin = false;
          } else {
            _decoded = decoded;
          }
        });
        if (isUser || isAdmin) {
          _data.id = _decoded;
          _data.userTokenType = isAdmin ? "admin" : "user";
          res._data = _data;
          next(); //中间件响应
        } else {
          res.send({
            result: -999,
            msg: "登录超时,请重新登录",
          });
        }
      };
    /* Crypto加密方法
       * @param {object} _data       对用户请求后端的参数进行加密
       */
      static setCrypto(_data) {
        let encrypted = cryptoJS.AES.encrypt(JSON.stringify(_data), key, {
          mode: cryptoJS.mode.ECB,
          padding: cryptoJS.pad.Pkcs7,
        });
        return encrypted.toString();
      }
     /* Crypto解密方法
       * @param {string} _token       将秘文解密成对象形式
       */
      static getCrypto(_token) {
        _token = decodeURIComponent(_token); //前端传参有特殊字符(中文)时转义(替换百分号)
        let decrypt = cryptoJS.AES.decrypt(_token, key, {
          mode: cryptoJS.mode.ECB,
          padding: cryptoJS.pad.Pkcs7,
        });
        return JSON.parse(cryptoJS.enc.Utf8.stringify(decrypt).toString());
      }
      static createBcrypt(password) {//加密密码
        return bcrypt.hashSync(password, bcrypt.genSaltSync(10));
      }
      static checkBcrypt(_password, _hash) {//对比密码
        return bcrypt.compareSync(_password, _hash);
      }
    }

    配置完成utils后进行路由配置,在routes文件夹下新建routes.js文件,并使用token验证用户是否正确

    const Util = require("../utils/utils");
    const Config = require("../config/config");
    module.exports = class Route {
      constructor(app) {
        app.get(Config.ServerApi.checkToken, Util.checkToken, (req, res) => {
          res.send({
            result: 1,
            msg: "验证成功",
            data: res._data
          });
        });
      }
    };

    配置config.js

    module.exports = {
      Agreement: "http://",//协议
      DataBaseUrl: "127.0.0.1",//ip或域名
      DataBasePort: ":27017",//数据库端口
      DataBaseName: "shopping",//数据库文档名称
      ServerUrl: "",
      ServerPort: ":1024",//服务端请求端口
      Path: "/",//路由名
      UserKey: "user",//用户token加密标识
      AdminKey: "admin",//管理员token加密标识
      CryptoKey: "tokenkey",//Crypto加密关键字,用于生成16进制秘钥
      ServerApi: {//接口名称
        checkToken: "/checkToken",//token验证
        userLogin: "/userLogin",//用户登录
      }
    }

    随后,新建用户管理路由接口,但是在此之前,我们需要配置一下数据库

    1. 下载Robo3t,并安装
    2. 新建数据库,这里我新建的数据库名称是Shopping
    3. 新建数据库表,这里我新建的是users表

    连接数据库

    1. 在model.js中新建数据库连接(原理观察者模式,类似与socket监听自定义事件)
      const mongoose = require('mongoose');
      const config = require('../../config/config')
      module.exports = class Mongoose {
          constructor() {
              mongoose.connect(`mongodb://${config.DataBaseUrl+config.DataBasePort+config.Path+config.DataBaseName}`, {
                  useNewUrlParser: true
              });
              this.db = mongoose.connection;
              this.db.on("error", function (error) {
                  console.log("Err:" + error);
              });
      
              this.db.on("open", function () {
                  console.log("Connet Success~");
              });
      
              this.db.on('disconnected', function () {
                  console.log('Connet Stop~');
              });
              return mongoose
          }
      }
    2. 新建数据库Schema用于连接数据表,这里我们直接将配置项剥离到config.js文件

      const _mongoose = require('./model');
      let mongoose = new _mongoose()
      const _schema = mongoose.Schema;
      module.exports = class Schema {
          constructor(config) {
              let schema = new _schema(config.data);
              let Model = mongoose.model(config.modelName, schema); //新建数据库
              return Model
          }
      }
    3. config.js中新增用户表默认字段配置

      Collections: {
          Users: {
            modelName: "users",
            data: {
              headPic: {//头像
                type: String,
                required: false,
                default: "public/assets/img/default.gif"
              },
              userType: {//用户类型(管理员/用户)
                type: String,
                required: true,
              },
              username: {//用户名
                type: String,
                required: true,
              },
              password: {//密码
                type: String,
                required: true
              },
              sex: {//性别
                type: String,
                required: true
              },
              mailaddress: {//邮箱地址
                type: String,
                required: true
              },
              mailurl: {//邮箱类型
                type: String,
                required: true
              },
              alladdress: {//省市县
                type: Array,
                required: false,
                default: []
              },
              address: {//具体地址
                type: String,
                required: false,
                default: ''
              },
              descript: {//个人说明
                type: String,
                required: false,
                default: ''
              },
              time: {//注册时间
                type: String,
                required: true
              },
              isactive: {//是否冻结用户
                type: Boolean,
                default: true
              }
            }
          }
        },

    以上是配置,token验证及登录的后端代码 

    总结

    将项目搭建成类似于mvc的设计思想很有必要,数据模型和控制层的剥离,可以使代码思路清晰,复用性以及可维护性提升

  • 相关阅读:
    多线程实践
    sql你server,mysql,oracle的分页语句
    BS与CS的联系与区别
    EJB与JAVA BEAN的区别
    Struts2.0 xml文件的配置(package,namespace,action)
    Q 51~60
    Q 41~50
    列表推导式
    Q 31~40
    Q 21~30
  • 原文地址:https://www.cnblogs.com/HelloWorld-Yu/p/13514440.html
Copyright © 2020-2023  润新知