本话概要
最近闲得发慌,赶紧写点博客打发时间。。
这期我将教大家【文件共享平台】的后端用户路由模块如何设计,包括用户表设计、用户注册、登陆、登出基本功能,以及信息查询。
该模块不仅仅是该平台用到,基本所有需要记录用户信息的系统都会涉及,而本期仅仅是教大家最基础的入门知识,欢迎指正!
用户表设计
我们在第四话已经教大家怎么操作mongodb,这里说的用户表也就是对应mongodb中的collection(集合)。
我们考虑下这个系统需要收集用户的什么信息。最基本的_id,用来区分每个用户,用户名name、用户密码password、用户角色role(权限问题)、用户是否注册isregister。这些都是最基本的,其他字段当然可以根据需要自行衍生。
我个人完整的表结构如图:
用户功能模块
我们首先在根目录的router文件夹下创建一个user.js路由模块(其实这一个文件,我已经在第二话配置路由中讲过,不清楚整个后端文件结构的朋友可以返回第二章了解一下~)。
user.js是被index.js引用的一个路由模块,专门用于书写用户相关的路由操作。
我们先在user.js中写如下代码:
const router = require('koa-router')();
const MongoOpr = require('../lib/dbOpr');
const MongoDB = new MongoOpr('User');
//具体代码往下看..
module.exports = router;
注册
用户注册需要往后台post所有用户信息,后台获取信息,先到数据库中判断是否存在该用户名,如果不存在,则注册,如存在,提示用户名已注册。具体代码如下:
//注册 POST localhost:3000/user/register
router.post('/register',async (ctx,next) => {
let name = ctx.request.body.name;//获取用户名
let pwd = ctx.request.body.password;//获取用户密码字段
let sex = ctx.request.body.sex;//获取用户性别字段
let res = await MongoDB.findMsg({key:['name'],value:[name]});//查询数据库有无该用户名的document
if(res.length == 0){//无该用户
MongoDB.insertMsg({
name:name,
password:pwd,
sex:sex,
history:[]
})
ctx.body = {
code:0,
msg:'已注册'
}
}else{//已注册
ctx.body = {
code:1,
msg:'该用户已注册,请更换用户名'
}
}
})
之前没有提到koa的路由具体怎么写,这里解释一下:
router.方法('路由路径',处理方法的中间件);
方法就是http的方法,如get、post、put、delete。
路由路径格式如下:http://localhost:3000/user+路由路径。
处理方法中间件是koa的一大特色,可以利用await next()进行中间件的切换。前提是方法必须带async,保证await可用。需要注意中间件的执行顺序和位置有关,按顺序执行,遇到next()执行下一个中间件函数,执行完后跳回来继续执行本函数后面的代码,直到给ctx.body赋值截止。
登陆
用户注册完后,查看数据库(用NosqlClient)应该会多一条记录,当然这里还没交大家前端搭建,可以先yy一下。。
注册后,我们要考虑登陆问题。登陆有一个特殊的地方就是,除了验证密码,还需要进行session存储。我们在第三话设置session中讲过原理。实现起来很简单,就一行,代码如下:
//登录 POST localhost:3000/user/login
router.post('/login',async (ctx,next) => {
let name = ctx.request.body.name;
let pwd = ctx.request.body.password;
let res = await MongoDB.findMsg({key:['name'],value:[name]});
if(res.length == 0){
ctx.body = {
code:2,
msg:'该用户不存在'
}
}else if(res[0].password == pwd){//验证密码正确性,这里密码没有加密,当然最好加密了再保存,这个可以自己了解
ctx.session.user = name;//设置session为用户名,当然,需要保证用户名都不同,你也可以用_id这种唯一值来设置session
ctx.body = {
code:0,
info:{
name:res[0].name,
role:res[0].role,
isgraduate:res[0].isgraduate
}
}
}else{
ctx.body = {
code:1,
msg:'密码错误'
}
}
})
如果登陆成功,用户浏览器中的cookie应该有一条session_id的记录,在访问后端每个链接中都会自动带上该cookie,以此验证用户登陆态。
登出
用户如果点击退出登陆,我们需要在后台清除该session,代码如下:
//登出 POST localhost:3000/user/logout
router.post('/logout',async (ctx,next) => {
// 将登录信息清空
ctx.session = null;
ctx.body = {
code:0,
msg:'已登出'
}
})
这一步会将存储在用户浏览器端的cookie中的session_id和服务端的session一起清空,所以用户再次访问路由就可以检测出未登陆状态。
返回所有用户信息
首先验证用户是否登陆,若是,则查询所有用户记录,筛选后返回。验证部分使用了一个中间件,因为仅有该模块需要验证登陆,所以直接放在该路由内部使用,代码如下:
//获取所有成员 GET localhost:3000/user/getAll
router.get('/getAll',async (ctx,next)=>{//验证登陆的中间件
if (!ctx.session.user) {//未登陆,返回403码
ctx.status = 403;
ctx.body = {
msg:'未登录'
}
}else{
await next();//执行下一个中间件
}},async(ctx,next)=>{//获取所有用户信息
let res = await MongoDB.findMsg({key:[],value:[]});
ctx.body = {
code:0,
users:res.map(item=>{//过滤掉敏感的字段,如密码
return {
name:item.name,
role:item.role,
sex:item.sex,
isregister:item.isregister
...
}
})
}
})
完整代码
const router = require('koa-router')();
const MongoOpr = require('../lib/dbOpr');
const MongoDB = new MongoOpr('User');
//注册 POST localhost:3000/user/register
router.post('/register',async (ctx,next) => {
let name = ctx.request.body.name;//获取用户名
let pwd = ctx.request.body.password;//获取用户密码字段
let sex = ctx.request.body.sex;//获取用户性别字段
let res = await MongoDB.findMsg({key:['name'],value:[name]});//查询数据库有无该用户名的document
if(res.length == 0){//无该用户
MongoDB.insertMsg({
name:name,
password:pwd,
sex:sex,
history:[]
})
ctx.body = {
code:0,
msg:'已注册'
}
}else{//已注册
ctx.body = {
code:1,
msg:'该用户已注册,请更换用户名'
}
}
})
//登录 POST localhost:3000/user/login
router.post('/login',async (ctx,next) => {
let name = ctx.request.body.name;
let pwd = ctx.request.body.password;
let res = await MongoDB.findMsg({key:['name'],value:[name]});
if(res.length == 0){
ctx.body = {
code:2,
msg:'该用户不存在'
}
}else if(res[0].password == pwd){//验证密码正确性,这里密码没有加密,当然最好加密了再保存,这个可以自己了解
ctx.session.user = name;//设置session为用户名,当然,需要保证用户名都不同,你也可以用_id这种唯一值来设置session
ctx.body = {
code:0,
info:{
name:res[0].name,
role:res[0].role,
isgraduate:res[0].isgraduate
}
}
}else{
ctx.body = {
code:1,
msg:'密码错误'
}
}
})
//登出 POST localhost:3000/user/logout
router.post('/logout',async (ctx,next) => {
// 将登录信息清空
ctx.session = null;
ctx.body = {
code:0,
msg:'已登出'
}
})
//获取所有成员 GET localhost:3000/user/getAll
router.get('/getAll',async (ctx,next)=>{//验证登陆的中间件
if (!ctx.session.user) {//未登陆,返回403码
ctx.status = 403;
ctx.body = {
msg:'未登录'
}
}else{
await next();//执行下一个中间件
}},async(ctx,next)=>{//获取所有用户信息
let res = await MongoDB.findMsg({key:[],value:[]});
ctx.body = {
code:0,
users:res.map(item=>{//过滤掉敏感的字段,如密码
return {
name:item.name,
role:item.role,
sex:item.sex,
isregister:item.isregister
...
}
})
}
})
module.exports = router;
下期预告
下一话我将教大家设计后端的文件和文件夹功能路由。今天就唠这么多啦,你是否有所收获呢?
我们下期再见,晚安🌝