前端我用的是vue+axios,项目地址nothing-left
项目结构
在用vue-cli构建项目脚手架的基础上新建一个app.js和server文件夹
1 2 3 4 5 6 7 8 9 10 11 12
   | ├─ app.js//koa入口文件 └─ server//后端        ├─ config      //数据库配置        │    └─ db.js        ├─ controllers //对models里面的方法进行选择性的组装,来响应前端不同的请求        │    └─ users.js        ├─ models      //为controllers提供一些方法        │    └─ users.js        ├─ routes      //定义路由,把前端不同的请求分配给controllers相对应的方法        │    └─ auth.js        └─ schema      //数据库表结构             └─ users.js
   | 
 
项目依赖
koa-router:对路由进行控制
koa-logger:日志模块,当前后端进行交互时会在控制台打印相关信息
koa-bodyparser:当前端发送post请求时,把请求里面的数据解析到ctx.request.body中
sequelize:用于后端操纵数据库
koa-jwt:用于实现基于JSON-WEB-TOKEN的登录验证
bcryptjs:用bcrypt的方式对密码进行加密和解密
app.js
app.js作为koa的启动文件。在app.js中,我们需要:
1.引入koa
1
   | const Koa = require('koa');
  | 
 
2.创建一个koa对象
3.引入koa中间件
1 2 3 4
   | const koaRouter = require('koa-router'); const logger = require('koa-logger'); const jwt =  require('koa-jwt'); const bodyparser = require('koa-bodyparser');
  | 
 
4.在koa对象中注册各中间件
1 2 3 4
   | app.use(bodyparser()); app.use(logger()); const router = koaRouter(); app.use(router.routes());
   | 
 
5.设置监听端口
1 2 3
   | app.listen(3001, () => {     console.log('Koa is listening in 3001'); });
  | 
 
6.将app暴露出去
搭建数据库
1.我采用可视化工具HediSQL来操作MySQL。在HediSQL上,我们先创建一个名为nothing-left的数据库,并创建一张用于存储用户信息的users表:
| 名称 | 
数据类型 | 
长度 | 
注释 | 
| id | 
INT | 
10 | 
用户的id | 
| username | 
VARCHAR | 
50 | 
用户名 | 
| password | 
VARCHAR | 
128 | 
密码 | 
2.接着,我们将users表导出到server文件夹下,形成users.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | module.exports = function(sequelize, DataTypes) {   return sequelize.define('users', {     username: {       type: DataTypes.STRING(50),       allowNull: false,     },     password: {       type: DataTypes.STRING(128),       allowNull: false     },     id: {       type: DataTypes.INTEGER(10),       primaryKey: true,       allowNull: false,       autoIncrement: true     }   }, {     tableName: 'users'   }); };
  | 
 
3.在config下新建db.js用于初始化Sequelize和数据库的连接。
1 2 3 4 5 6 7 8 9 10 11 12
   | const Sequelize = require('sequelize');
 
  const NothingLeft = new Sequelize(`mysql://root:xxx@localhost/nothing-left`,{   define:{     timestamps:false   } });
  module.exports = {   NothingLeft }
  | 
 
models
在models文件夹下我们新建users.js用于定义一些对数据库进行各种操作的方法。在users.js文件里,我们需要定义两个方法,一个是通过用户名查找到某个用户的信息,另一个是创建新的用户。
1.首先,我们需要在users.js里引入数据库和users表,并用sequelize去实例化一个Users对象:
1 2 3 4
   | const db = require('../config/db.js'); const usersModel = '../schema/users.js'; const NothingLeftDB = db.NothingLeft; const Users = NothingLeftDB.import(usersModel);
  | 
 
2.接着,我们来定义一个通过用户名查找用户信息的方法:
1 2 3 4 5 6 7 8 9
   | const getUserByName = async function (name) {   const userInfo = await Users.findOne({     where:{       username:name     }   })
    return userInfo; };
  | 
 
 async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
对于其中牵涉到的promise和Generator方面的知识,暂时理解还不够。
3.定义一个用于创建新用户的方法
1 2 3 4 5 6 7 8 9 10
   | const bcrypt = require('bcryptjs') const createUser = async function (data) {   const password = bcrypt.hashSync(data.password, bcrypt.genSaltSync(10))   await Users.create({     id: 1,     username: data.userName,     password: password   })         return 200 };
  | 
 
在这里,我们需要引入bcryptjs为我们的密码在存入数据库前进行加密
4.将方法们暴露出去供controllers调用
1 2 3 4
   | module.exports = {   createUser,   getUserByName };
  | 
 
controllers
1.引入相关依赖和models/users.js
1 2 3
   | const users = require('../models/users.js'); const jwt = require('jsonwebtoken');  const bcrypt = require('bcryptjs');
  | 
 
2.实现登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
   | const postUserAuth = async function (ctx) {   const data = ctx.request.body;    const userInfo = await users.getUserByName(data.name)   if (userInfo != null) {      if (!bcrypt.compareSync(data.password, userInfo.password)) {     ctx.body = {       code: 202,        info: '密码错误!'     }     } else {       const userToken = {         name: userInfo.username,         id: userInfo.id       }       const secret = 'nothing-left'        const token = jwt.sign(userToken, secret)        ctx.body = {         code: 200,         token: token        }     }   } else {     ctx.body = {     code: 201,     info: '用户不存在!'      }   } };
  | 
 
当postUserAuth接收到前端post过来的数据时,会调用models里面的方法查看该用户名是否存在。如果不存在,则返回相关信息。若存在,postUserAuth会将前端post过来的密码和数据库中该用户名对应的密码进行比较,若密码一致,就签发token,否则返回相关信息。
3.实现注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | const register = async function (ctx) {   const data = ctx.request.body   const sameUserName = await users.getUserByName(data.userName)   if(sameUserName == null) {     await users.createUser(data)     ctx.body = {       code: 200     }   } else {     ctx.body = {       code: 201     }   } };
  | 
 
4.将方法导出
1 2 3 4
   | module.exports = {   register,   postUserAuth }
  | 
 
定义路由
在routes文件夹下新建auth.js
1 2 3 4 5 6 7 8 9 10
   | const auth = require('../controllers/users.js'); const koaRouter = require('koa-router'); const router = koaRouter();
  router.post('/users', auth.postUserAuth) router.post('/users/register', auth.register)
  module.exports = {     router };
  | 
 
跳转拦截
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   |  router.beforeEach((to, from, next) => {   const token = sessionStorage.getItem('demo-token')   if (to.path === '/') {      if (token !== 'null' && token !== null) {       next('/home')       }     next()    } else {     if (token !== 'null' && token !== null || to.path === '/register') {       Vue.prototype.$http.defaults.headers.common['Authorization'] = 'Bearer ' + token        next()      } else {       next('/')      }    } });
 
  | 
 
这个时候,从后端给我们传回来的token就派上大用处。有token就说明我们的身份是经过验证的,否则就是非法的。
vue-router提供了页面跳转的钩子,我们可以在router跳转前进行验证,如果token存在就跳转,如果不存在就返回登录页。
解析token
1 2
   |  router.use('/api', jwt({secret: 'nothing-left'}), api.router.routes());
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
   | 
  login: function() {   if (!this.checkMessage()) {     return   }         let obj = {     name: this.cellphone,     password: this.password   }   const result = this.$http.post('/auth/users', obj);   result.then((res) => {    switch(res.data.code) {
      case 200:     this.isLoading=true;     sessionStorage.setItem('demo-token', res.data.token)     this.$router.push('/home');     break;
      case 201:     this.showSnackbar("用户名不存在!");     sessionStorage.setItem('demo-token', null)     break;
      case 202:     this.showSnackbar("密码不正确!");     sessionStorage.setItem('demo-token', null)     break;
    }            }, (err) => {     console.log(err)             sessionStorage.setItem('demo-token', null)              })             return result           }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   |  regist: function() {                   if (!this.checkMessage()) {     return   }   let obj = {     userName: this.cellphone,     email: this.code,     password: this.firstPassword   }   const result = this.$http.post('/auth/users/register', obj)   result.then((res) => {    switch(res.data.code) {     case 200:     this.showSnackbar("注册成功")     this.$router.push('/')     break
      case 201:     this.showSnackbar("该用户名已被注册")     break   }   }, (err) => {     console.log(err)     })   }
 
  | 
 
参考项目/文章