一,重构用户创建
在上篇文章(https://www.jianshu.com/p/42bc94ab7f69)中我们实现了用户的创建功能,存储用户信息的时候只有一个用户信息表,用户信息表的键为user:[userId],是一个和用户ID相关的键。用户在登录的时候输入的是用户名,如何根据用户名找到其键值进而找到用户信息呢?
为了解决这个问题,我们需要在创建用户时需要创建一个用户名和用户ID的对应表,这样的话我们在注册时需要判断用户名是否已经注册过了。即在我们这个系统中用户名不可重复。
下面开始我们的改造:
- 定义用户名和用户ID映射键
在constData.js中添加一行定义:
'UNAME_UID_MAP_KEY' : 'uname:uid',
此键值对应值的数据结构为hash,field为用户名,value为userId。 - 重构创建用户函数
在创建用户时首先检测是否已经存在用户名和用户ID的对应关系了,如果存在则此用户名不可用,不存在则创建用户,同时记录用户名和用户ID的对应关系。
在user.js中重构createUser如下:
user.createUser = function(userName, passWord, callBack) {
var createUser = function(userId) {
// 获取到用户ID后创建用户
var userKey = constData.USER_BASEK_KEY + userId;
var userData = {
'name' : userName,
'passWord' : passWord,
'coin' : 0,
'diamond' : 0,
'head' : '',
'friends':[]
}
// 数据库创建用户
redisDB.hmset(userKey, userData, function(err, result) {
var data = {
'userId' : userId,
'name' : userName,
'token' : ''
}
// 创建用户名和id的映射
redisDB.hset(constData.UNAME_UID_MAP_KEY, userName, userId);
if(err) {
data = {
'error' : err,
'note' : 'create user error!!'
}
callBack(data);
return;
}
callBack(data);
})
}
var getUserId = function() {
redisDB.incrby(constData.GLOBAL_USER_ID, 1, function(err, userId) {
if(err) {
var data = {
'error' : err,
'note' : 'create user error!!'
}
callBack(data);
return;
}
createUser(userId);
});
}
// 检验用户名是否存在
redisDB.hget(constData.UNAME_UID_MAP_KEY, userName, function(err, result) {
if(result) {
data = {
'error' : 1,
'note' : 'UserName exist'
}
callBack && callBack(data);
return;
}
getUserId();
});
}
这样,在登录的时候我们可以根据用户名找到用户ID,进而找到所有的用户信息。
二,开发前端登录功能
1, UI
登录UI包含以下几个控件:
- 用户名输入框
- 密码输入框
- 登录按钮
-
输入错误提示信息
如下图所示:
01.png
2,代码开发
- 定义登录路径
在Conatnt.js中加入以下定义:
Constant.LOGIN_URL = baseURL + 'login/';
- 登录逻辑开发
在Script/Scene文件夹下新建login.js文件,首先引入文件:
var http = require('http');
var Constant = require('Constant');
定义相关变量引用:
passWordOrUserNameIsNull: {
default: null,
type: cc.Label
},
passWordErrorHint: {
default: null,
type: cc.Label
},
userName: '',
passWd: ''
在定义上相关变量后,在编辑器中进行变量和控件的对应关系操作。
进行输入用户名/密码完毕后的操作:
inputUserNameEnded: function(editbox, customEventData) {
this.userName = editbox.string;
},
inputPassWordEnded: function(editbox, customEventData) {
this.passWd = editbox.string;
},
再将其和编辑器中的控件对应起来。
实现输入错误时的提示函数:
_showErrorHint: function(label) {
this.passWordErrorHint.node.active = false;
this.passWordOrUserNameIsNull.node.active = false;
// 显示提示
label.node.active = true;
// 设置计时器
this.scheduleOnce(function(){
label.node.active = false;
}.bind(this), 2);
},
登录按钮测回调函数
onLogin: function() {
if (!this.userName || this.userName.length<1) {
console.log('onLogin come in 000');
this._showErrorHint(this.passWordOrUserNameIsNull);
return;
}
if (!this.passWd || this.passWd.length<1) {
console.log('onLogin come in 001 this.passWd = ' + this.passWd);
this._showErrorHint(this.passWordErrorHint);
return;
}
this._startLogin();
},
将其和登录按钮对应起来。
登录函数的实现
_startLogin: function() {
var obj = {
'url' : Constant.LOGIN_URL,
'data' : {
'userName' : this.userName,
'passWord' : this.passWd
},
'success' : function(jsonData) {
this._onLoginSuccess(jsonData);
}.bind(this),
'fail' : function() {
this._onLoginFail(jsonData);
}.bind(this)
}
http.request(obj);
},
登录回调函数的实现
本次只把登录结果信息打印出来:
_onLoginSuccess: function(jsonData) {
console.log('_onLoginSuccess come in jsonData = ' + JSON.stringify(jsonData));
},
_onLoginFail: function(jsonData) {
console.log('_onLoginFail come in jsonData = ' + JSON.stringify(jsonData));
},
三,后端功能实现
1,实现生成token功能
在constData.js中添加用户ID和token映射的键:
'USER_TOKEN_KEY' : 'uid:token'
此键对应的值为hash,field为用户ID,值为用户的token。
我们的token使用一组随机数加用户ID和时间等信息组成:
var randomInt = function() {
return Math.ceil(Math.random() * 9999999);
}
var formatDate = function() {
var d = new Date();
return d.getFullYear() + '-' + d.getMonth() + '-' + d.getDay() + ' ' + d.getHours()+':'+d.getMinutes()+':'+d.getSeconds() + ' ' + d.getMilliseconds();
}
var setToken = function(userId) {
var token = '' + randomInt() + '|' + userId + '|' + randomInt() + '|' + formatDate() + '|' + randomInt();
redisDB.hset(constData.USER_TOKEN_KEY, userId, token);
return token;
}
2,实现用户登录逻辑
在用户登录时我们后端需要做的事:
- 根据用户名找到用户ID
- 根据用户ID找到用户信息
- 判断用户输入的密码和找到的用户信息中存储的用户密码是否一致
- 返回结果
在user.js文件中添加入如下函数
user.login = function(userName, passWord, callBack) {
redisDB.hget(constData.UNAME_UID_MAP_KEY, userName, function(err, result) {
if(!result) {
data = {
'error' : 1,
'note' : 'UserName not exist'
}
callBack && callBack(data);
return;
}
// 获取用户
var userKey = constData.USER_BASEK_KEY + result;
var dataElements = ['passWord', 'name', 'coin', 'diamond', 'head', 'friends'];
redisDB.hmget(userKey, dataElements, function(err, result) {
if (err) {
var data = {
'error' : 1,
'note' : 'get userinfo error!'
}
callBack && callBack(data);
return;
}
//判断用户密码是否相同
var userData = {};
for(var i=0, l=dataElements.length; i<l; i++) {
userData[dataElements[i]] = result[i];
}
// 设置用户token
userData['token'] = setToken(result);
if(userData['passWord'] == passWord) {
var data = {
'error' : 0,
'data' : userData
}
callBack && callBack(data);
return;
}
data = {
'error' : 1,
'note' : 'Error password'
}
callBack && callBack(data);
});
});
}
3,路由映射
在router.js中添加登录功能的处理:
router.login = function(req, res, callBack) {
var params = getParams(req);
//登录
user.login(params['userName'], params['passWord'], function(data) {
callBack && callBack(data);
})
}
至此,我们就完成了用户登录过程。
上一篇 实现用户注册
下一篇 实现Socket通信
网友评论