vue + express + mongodb 搭建一个后台登录系统

需要准备的

首先安装这些东西

nodeJs , npm

image

mongodb , adminMongo(非必须)

image

image

vue-element-admin

express

前端的页面我决定用vue-element-admin,git clone 后就可以看到完整的开发框架

image

前端登录思路

验证思路是登录成功后,服务端返回 token(标识用户的唯一身份),之后将 token 储存在本地 cookie 中,下次打开页面或者刷新页面就能记住用户的登录状态。

在用户登录成功后,会在全局钩子 router.beforeEach,判断是否已获得 token,在获得 token 之后去获取用户的信息。

下面看具体登录页的主要代码

//index.vue
handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          this.$store.dispatch('LoginByUsername', this.loginForm).then(() => {
            this.loading = false
            this.$router.push({ path: this.redirect || '/' })
          }).catch(() => {
            this.loading = false
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    },

可以点击登录后,先进行了表单验证,然后触发了 vuex 的 LoginByUsername 的 action,
从代码中搜索一下这个异步操作做了什么

// user.js
import { loginByUsername, logout, getUserInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
actions: {
LoginByUsername({ commit }, userInfo) {
      const username = userInfo.username.trim()
      return new Promise((resolve, reject) => {
        loginByUsername(username, userInfo.password).then(response => {
          const data = response.data
          commit('SET_TOKEN', data.token)
          setToken(response.data.token)
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
    },
    ...

可以看到这个 LoginByUsername 中调用了 login.js 的 loginByUsername 方法,loginByUsername 接受了 username 和 password 两个参数。可以推测 loginByUsername 是发送了请求,在请求完毕之后提交了 SET_TOKEN 这种方法 ,并且调用了 auth.js 中的 setToken

// login.js
import request from '@/utils/request'
export function loginByUsername(username, password) {
  const data = {
    username,
    password
  }
  return request({
    url: '/login/login',
    method: 'post',
    data
  })
}

可以看到这里是发送了请求,其中 request 应该是封装了 axios 的方法,具体怎么封装后面再看。

// user.js
 mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },

可以看到 SET_TOKEN 就是将请求返回的 token 值更新到 vuex 的 state 中。

//auth.js
import Cookies from 'js-cookie'
const TokenKey = 'Admin-Token'
export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

setToken 方法是把 token 存进 cookie 中

后台思路

按照前端的逻辑,我们需要有一个登录的接口,登录的时候从数据库中查询,如果正确就返回 token 给前端,还需要有一个接口来接收 token,返回给前端用户的具体信息。

express

先从express开始说

通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。生成后应该可以看到下面的目录结构。
image

npm install //安装依赖模块
npm start //启动项目

因为每次修改之后还需要重新 npm start 会比较麻烦,所以我用nodemon来进行热更新。

npm install -g nodemon //安装nodemon

image

就能启动 express 应用了

image

下面看 express 项目的结构

bin 是应用的启动目录,

var http = require('http');
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
var server = http.createServer(app);
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

public 是项目的静态文件目录

routes 控制路由,我们需要在这里做登录的接口

views 是视图文件,我们在这里放 html 文件

mongodb

注册登录需要数据库,我使用了 mongodb ,用mongoose
操作数据库,文档有介绍的就不啰嗦了。

在 express 的目录下建立一个 database 文件夹,建立文件 model.js 以及处理文件 bdHandler.js

// models.js
module.exports = {
    user:{
        username:{type:String,required:true},
        password:{type:String,required:true},
        roles:{type:String,required:true}
    }
};

//bdHandel.js
//提供其他文件对model的操作
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var models = require("./models");

for(var m in models){
    mongoose.model(m,new Schema(models[m]));
}

module.exports = {
    getModel: function(type){
        return _getModel(type);
    }
};

var _getModel = function(type){
    return mongoose.model(type);
};

然后我们需要链接到数据库,并把 bdHandel 设置为全局的以便于在路由中使用

//在app.js中添加
global.dbHandel = require('./database/dbHandler');
global.db = mongoose.connect("mongodb://localhost:27017/ch_users", { useNewUrlParser: true })
后台接口

做完后,我们就可以去处理登录的接口部分了

var express = require('express');
var router = express.Router();
var dbhandler = require('../database/dbHandler');
var jwt = require('jwt-simple');
router.post("/login", function (req, res) {
// 从此路径检测到post方式则进行post数据的处理操作
  //这里的User就是从model中获取user对象
  var User = dbhandler.getModel('user');
  var {username} = req.body;                //获取post上来的 data数据中 username

  User.findOne({ username }, function (err, doc) {   //通过此model以用户名的条件 查询数据库中的匹配信息
    if (err) {                            //错误就返回 状态码为500的错误
      res.send(500);
      console.log(err);
    } else if (!doc) {                                 //查询不到用户名匹配信息,则用户名不存在
      res.send({code:400,message:'用户名不存在'});                            //    状态码返回404
    } else {
      if (req.body.password != doc.password) {     //查询到匹配用户名的信息,但相应的password属性不匹配
        res.send({code:400,message:'密码错误'});
      } else {
      //信息匹配成功返回20000,返回token
        let token = jwt.encode(doc, secret);
        res.send({code:20000,token});
      }
    }
  })
})
module.exports = router;

其中用户认证用的是基于 JWT 身份验证的方法,其中的 secret 是存在服务器上的密码,为了方便我把它作为了一个全局变量

//app.js
global.secret = 'yosgi'

别忘了 router 还需要在 app.js 中注册

//app.js
var users = require('./routes/users');
app.use('/login', users);

这样我们的登录接口就做完了,在前端页面试试吧。

前端登录页面

我们的 express 应用监听的是 localhost:3000,因此我们需要往 localhost:3000/login/login
的地址发送 POST 请求。

首先要把 mock 中的模拟请求去掉,才能真正发送请求。

// mock\index.js
// Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
这一段注释掉

点击登录尝试一下 发现是在向

http://serve-dev/login/login

发送请求,猜测是 axios 方面的 baseURL 设置的,在 utils/request.js 中看到下面的代码

//request.js
import axios from 'axios'
const service = axios.create({
  baseURL: process.env.BASE_API, // api 的 base_url
  timeout: 5000 // request timeout
})

在 config 的 dev.env.js 中去掉

//dev.env.js
module.exports = {
  NODE_ENV: '"development"',
  ENV_CONFIG: '"dev"',
  BASE_API: ''
}

现在发送请求的地址是

http://localhost:9527/login/login

为什么不直接把 BASE_API 改成 http://localhost:3000 呢?因为这样的话就会跨域,跨域可以后台用 cors 解决,不过我这边决定还是用 proxyTable 插件比较方便。

// index.js
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
  '/login/login':{
      target: 'http://localhost:3000',// 3000 端口启动的本地服务器
      changeOrigin: true
  }
}

这样就把向/login/login 的请求发送到了 http://localhost:3000/login/login 上了
尝试一下

我们事先在数据库做好了 admin 用户
image

请求结果

image

image

这步成功后,token 就被储存到了 vuex 以及 cookie 中。接下来需要做的是

  1. 使用 token 进行验证,拉取用户信息
  2. 做注册用户的接口
  3. 做登出的接口

没错我弃坑了!