基于Node.js和Cocos Creator的开发【四,实现用户登录】

一,重构用户创建

在上篇文章(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通信

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容

  • 一,前端界面设计 在注册界面设计一个比较简单的界面:一个输入用户名的输入框、一个输入密码和一个确认密码的输入框,最...
    Woodlouse阅读 3,996评论 0 2
  • 点击查看原文 Web SDK 开发手册 SDK 概述 网易云信 SDK 为 Web 应用提供一个完善的 IM 系统...
    layjoy阅读 13,745评论 0 15
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 我工作的地方,是个繁华的十字路口,周围都是商厦和写字楼。在西南角,有一条小街,狭长而简陋。铺面林立,街道很窄,行道...
    黄小丽的私人订制阅读 253评论 1 2
  • 昨天遇到一个讹诈的人,对她的这种现象我很是无语。对于这件事情,我已经是做到仁至义尽了,但对方还要故意找借口讹诈我,...
    tangchuntao唐糖阅读 331评论 4 5