前端我用的是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) }) }
|
参考项目/文章