通过阅读Popush前端代码,又可以加深对backbone以及socket.io的认识。感谢队友配置好了Linux下的环境,终于可以成功运行了。
和注册登录相关的代码主要有以下几个部分。在register-view.js中,有
<pre name="code" class="html">/*注册视图*/
var app = app || {};
(function () {
'use strict';
app.RegisterView = Backbone.View.extend({
el: '#register',
events: {
'keypress #register-inputName': 'registerOnEnter',
'keypress #register-inputPassword': 'registerOnEnter',
'keypress #register-confirmPassword': 'registerOnEnter',
'click #register-submit': 'register',
},
register: function () {
var name = $('#register-inputName').val(),
in_pw1 = $('#register-inputPassword'),
in_pw2 = $('#register-confirmPassword');
var pass = in_pw1.val();
var confirm = in_pw2.val();
in_pw1.val('');
in_pw2.val('');
var id = '#register-message',
str = '';
if (!/^[A-Za-z0-9]*$/.test(name)) {
str = 'name invalid';
} else if (name.length < 6 || name.length > 20) {
str = 'namelength';
} else if (pass.length > 31) {
str = 'passlength';
} else if (pass != confirm) {
str = 'doesntmatch';
}
if (str) {
app.showMessageBar(id, str);
} else if (app.Lock.attach({
loading: '#register-control',
error: function (data) {
app.showMessageBar(id, data.err, 'error');
},
success: function () {
app.showMessageBar(id, 'registerok');
},
})) {
app.socket.emit('register', {
name: name,
password: pass,
});
}
},
/*快捷键回车注册*/
registerOnEnter: function (e) {
if (e.which == 13) {
this.register();
}
},
show: function () {
$('#register-inputPassword').val('');
$('#register-confirmPassword').val('');
$('#register-message').hide();
$('#register-padding').slideDown('fast');
$('#register-inputName').focus();
},
});
app.init || (app.init = {});
app.init.registerView = function () {
if (app.views['register']) {
return;
}
app.views['register'] = new app.RegisterView();
};
})();
可以看到,当接收到“注册“的信息(由enter键或点击窗口的”注册“键触发)后,程序会发送”register'的信号。由app.js中的函数来接受socket的信号:
socket.on('register', function(data) { // name, password
if(!check(data, 'name', 'password')){
return;
}
userDAO.register(data.name, data.password, data.avatar || 'images/character.png', 'user', function(err){
socket.emit('register', {err:err});
});
});
也就是接收信号,将data存储到数据库中。这里的userDAO.register干的就是将接收到的信息存储到数据库中的工作。至于userDAO,看到下方的源代码可以更清楚地看到数据库做了什么。
因为我们想要实现注册后自动登录,所以这里关心的是它要在哪里完成了验证注册,之后又触发了什么事件。注册登录,其实只需要在看到它正常验证,将信息储存到数据库中,然后用该注册信息进行登录就可以了。下方是models里对于数据库交互的部分,其中的md5是一种加密函数。
/models/userDAO.js
UserDAO.prototype.register = function(name, password, avatar, group, callback){
if(!validateName(name)){
return callback("name invalid");
}
if(password.length > 32){
return callback("password too long");
}
lock.acquire(name, function(){
db.user.findOne({name:name}, {_id:1}, function(err, user){
if(err){
lock.release(name);
return callback("inner error");
}
if(user){
lock.release(name);
return callback("name exists");
}
db.user.insert({
name:name,
password:md5(xor(md5(name), md5(password))),
avatar:avatar,
state:"normal",
group:group,
docs:[],
createTime: new Date().getTime()
},
function(err, newUser){
if(err){
lock.release(name);
return callback("inner error");
}
else if(!newUser){
lock.release(name);
return callback("inner error");
}
else{
lock.release(name);
return callback(null);
}
});
});
});
};
UserDAO.prototype.login = function(name, password, ip, callback){
var that = this;
db.user.findOne({name:name}, function(err, user){
if(err){
return callback("inner error");
}
if(!user){
return callback("unauthorized");
}
if(md5(xor(md5(name), md5(password))) == user.password){
delete user.password;
db.user.update({_id:user._id}, {
$set:{
loginTime:new Date().getTime(),
loginIP:ip
}
}, function(err){
if(err){
return callback("inner error");
}
db.doc.find({_id : {$in:user.docs}}, {revisions:0,_id:0}, function(err, docs){
if (err){
return callback("inner error");
}
if (docs.length == 0){
docs = [];
user.docs = docs;
return callback(null,user);
}
else{
var counter = 0;
function t(doc){
db.user.findOne({_id:doc.owner},{name:1,_id:0,avatar:1},function(err,trueowner){
if (err){
that.innerError = true;
}
doc.owner = trueowner;
db.user.find({_id:{$in:doc.members}},{name:1,_id:0,avatar:1},function(err,members){
if (err){
that.innerError = true;
}
doc.members = members;
counter++;
if (counter == docs.length){
user.docs = docs;
return callback(null ,user);
}
});
});
}
for (i in docs){
if (that.innerError){
that.innerError = false;
return callback("inner error");
}
t(docs[i]);
}
}
});
});
}else{
return callback("unauthorized");
}
});
};
因此,问题的关键就在于 register-view.js中的这一部分代码:
else if(app.Lock.attach({
loading: '#register-control',
error: function (data) {
app.showMessageBar(id, data.err, 'error');
},
success: function () {
app.showMessageBar(id, 'registerok');
},
})) {
app.socket.emit('register', {
name: name,
password: pass,
});
也就是说,当它发送注册信息之后,如果失败了,就会显示MessageBar(id, data.err, 'error'),对应过去找这个函数可以看到有哪些错误。而如果成功了,就可以显示成功的message bar——app.showMessageBar(id, 'registerok');
我的想法很简单,只要将app.showMessageBar(id, 'registerofk')改为发送登录信息就可以了。修改之后的代码如下:
else if (app.Lock.attach({
loading: '#register-control',
error: function (data) {
app.showMessageBar(id, data.err, 'error');
},
success: function () {
<span style="white-space:pre"> </span> //register is ok
app.socket.emit('login', {
name: name,
password: pass,
});
},
})) {
app.socket.emit('register', {
name: name,
password: pass,
});
}
尝试一下,基础的注册后自动登录就完成了。但是这样还会有一个小小的问题——效率。这样的效率并不是最优的,因为我们知道在login的时候,其实是还有一步验证检查的。下面的一段代码来自于static/app.js。第一句的if(!check(data, 'name', 'password')就是对姓名和密码进行核对。
由于是刚刚注册过,其实是不需要这样的核对的。
socket.on('login', function(data){ // name, password
if(!check(data, 'name', 'password')){
return;
}
userDAO.login(data.name, data.password, ip, function(err, user){
if(err){
return socket.emit('login', {err:err});
}
var sid;
while(sid = crypto.randomBytes(32).toString('base64')){
if(!session[sid])
break;
}
socket.session = session[sid] = {user:user, sid:sid};
if(users[user.name]){
delete session[users[user.name].session.sid];
delete users[user.name].session;
users[user.name].emit('unauthorized');
}
users[user.name] = socket;
socket.emit('login', socket.session);
});
});
在之后可以再尝试进行优化。