前面已经实现了不同地址渲染不同页面的功能,后面我们需要实现注册和登录时的验证功能。
处理请求体
首先我们要知道,注册和登录页面中的姓名和密码都会通过post方式传递给后台。无论是node.js还是koa都无法解析request对象上的post数据,需要中间件解析,所以我们先引入koa-bodyparser
并且在router之前使用(保证router接收到的请求是解析过的):
1 2
| const bodyParser = require("koa-bodyparser"); app.use(bodyParser());
|
处理注册
接下来我们要处理页面的注册。这里涉及几个判断:
这些判断我们单独放置一个文件里,尽量保持主文件app.js的简洁。
- 先在根目录下创建一个
routes
文件夹,用来放置处理url的user_router.js
文件(把原来app.js中的router内容挪过来
1 2 3 4 5 6 7 8 9
| const Router = require("koa-router"); let userRouter = new Router();
userRouter.get('/register', ctx => { ctx.render('register'); }) .get('/login', ctx => { ctx.render('login') })
|
- 紧接着我们要在
userRouter
中加入对post方式提交的数据的判断,我们在user_controller.js
文件中定义判断方法checkUsername
和doRegister
1 2 3 4 5 6 7 8 9 10 11 12 13
| let userController = require("../controllers/user_controller");
userRouter.get('/user/register', ctx => { ctx.render('register'); }) .get('/user/login', ctx => { ctx.render('login') }) .post('/user/check-username', userController.checkUsername) .post('/user/do-register', userController.doRegister);
module.exports = router;
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const user_db = require('../modules/db');
exports.checkUsername = async(ctx) => { let {username} = ctx.request.body; let users = await user_db.query("SELECT * FROM user_table WHERE username = ?", [username]); if(users.length !== 0) { return ctx.body = {code: '001', msg: '用户名已存在'} } ctx.body = {code: '000', msg: '可以注册'} }
|
我们可以现在postman中测试一下结果:
首先测试一下使用已经注册过的username来尝试再次注册,post请求后,结果显示:
我们再试试改一个username来测试:
这次的结果:
接下来我们可以接着往下来判断如何才算注册成功:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| exports.doRegister = async ctx => { let {username, password, email} = ctx.quest.body; let users = await user_db.query("SELECT * FROM user_table WHERE id is ? or email = ?", [username, email]); let user; if(users.length !== 0) { if(users.length > 1) return ctx.body = {code: '004', msg: "用户名与邮箱都存在"}; user = users[0]; if(user.username === username && user.email === email) return ctx.body = {code: '004', msg: "用户名与邮箱都存在"}; if(user.username === username) return ctx.body = {code: '002', msg: "用户名已存在"}; if(user.email === email) return ctx.body = {code: '003', msg: "邮箱已存在"}; } let result = await user_db.query("INSERT INTO user_table (username, password, email) value (?, ?, ?)", [username, password, email]); ctx.body = {code: '001', msg: "注册成功"}; }
|
此时我们再用postman来测试一下我们的注册功能试试:
结果显示是成功:
我们再去数据库看看是否真的添加了新的数据:
(id因为我测试的时候添加过两条数据,所以直接跳到了4)
那么我们就完成了注册的功能。
处理登录
接下来我们处理登录。登录主要是判断两件事:
我们沿着前面的思路继续。首先在userRouter
上明确接下来要进行登录操作,所以要调用判断登录是否成功的函数:
1 2 3 4 5 6 7 8 9 10
| userRouter.get('/user/register', ctx => { ctx.render('register'); }) .get('/user/login', ctx => { ctx.render('login') }) .post('/user/check-username', userController.checkUsername) .post('/user/do-register', userController.doRegister) .post('/user/do-login', userController.doLogin)
|
然后再明确判断登录的内容:
1 2 3 4 5 6 7 8 9 10 11 12
| exports.doLogin = async ctx => { let {username, password, remember_me} = ctx.request.body; let users = await user_db.query("SELECT * FROM user_table WHERE username = ?", [username]); if(users.length === 0) return ctx.body = {code:'002', msg: "用户名或密码不正确"}; let user = users[0]; if(user.password !== password) return ctx.body = {code: '002', msg: "用户名或密码不正确"}; ctx.body = {code: '001', msg: "登录成功"}; }
|
这里住一个小的点,其实在判断中,我们第一个判断的是用户名是否存在,第二个是密码是否匹配,但是在返回给前端的信息中,我们最好不要细分,而是统一返回“用户名或密码不正确”,这样可以避免有人恶意尝试,反复提交请求。
验证登录
处理登录还有一个重要步骤,就是有些页面需要登录后才能使用,比如自己的音乐页面只有根据自己的id才会选择将自己上传的音乐显示出来,也就是说,才登录页面之后的操作需要记录我们的登录状态才可以继续进行。这里就需要用到session这个功能。
下载
我们还是先给项目下载中间件koa-session
配置
我们先给router
加上检查session这个动作
1 2 3 4 5 6 7 8 9 10 11
| userRouter.get('/user/register', ctx => { ctx.render('register'); }) .get('/user/login', ctx => { ctx.render('login') }) .post('/user/check-username', userController.checkUsername) .post('/user/do-register', userController.doRegister) .post('/user/do-login', userController.doLogin) .post("user/test-session", userController.test)
|
然后来规定怎么检测:
第一步是我们登录的时候,要先存一个session
1 2 3 4 5 6
| exports.doLogin = async ctx => { ctx.session.user = user; ctx.body = {code:"001", msg: "登录成功"}; }
|
我们可以把session对象打出来看一看,到底是一个什么样的数据:
事实上,session就是一个对象,从数据库中选出的符合用户名和密码的这个数据是对象的key,保存的具体数据是value。
第二部是检测session
1 2 3 4
| exports.test = async ctx =>{ ctx.body = ctx.session.user; }
|
并且在app.js
中调用中间件:
1 2 3 4
| const session = require("koa-session"); app.keys = ["hello world"]; app.use(session(app));
|
我们可以在postman中看到检测结果:
这里有一个需要注意的点,在我们do-login
时,浏览器有两个cookie,一个是原cookie,一个是签名过的。
之前在cookie和session的内容中提到过,签名的作用是防止cookie被篡改,但是无法防止cookie不被别人看到,我们将原cookie使用base64解码,其实能够轻易获得用户的数据:
这样当然存在安全隐患,所以我们应该将用户数据存在服务器端,浏览器和服务器间的通讯只携带签名过的这个session_id。
koa-session
中有一个将session数据存储在服务器端的选项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let store = { storage: {}, set: function(key, sess) { this.storage[key] = sess; }, get: function(key) { return this.storage[key]; }, destroy: function(key){ delete this.storage[key]; } } app.use(session({store}, app));
|
这时我们再试试登录,就不会返回原cookie了
此时的cookie都无法解码。但是test-session
还是可以获取用户信息:
最后,我们给用户请求加一个判断,前面我们把用户的注册、登录请求的地址都设置为/user/...
,方便我们来区分,接下来读取音乐列表、上传音乐等功能我们就不用/user
作为url的开头了,同时也需要确定用户是登录状态才能打开页面,因此我们在app.js文件中加上一个对url的判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| app.use(async (ctx, next) => { let regex = /^\/user/; let checkUrl = regex.test(ctx.request.url); if(checkUrl) return await next(); if(!ctx.session.user) { return ctx.body = `<div> <a href = '/login'>请登录</a> </div>`; } await next(); })
|
这时我们直接访问localhost:9000/index
就会被拒绝: