Tutorials‎ > ‎

Fast Login System using Node.js

posted Dec 28, 2011, 4:40 AM by Reza Aggi Prasetyo   [ updated Aug 15, 2016, 11:29 PM by Surya Wang ]

Node.js

In the previous article, I gave an example how to make a Node.js application that can host a simple web application files. Basically, Node.js is not an application that build only for web development, but one of its modules (the HTTP module), could help us much in developing a web application server using Node. Like the previous article, this article uses the v0.5.9 of Node.js windows executable program that can be downloaded here.  

Handle the Request

A Node application is an application that always has its instance until the application exit. Optimizing that feature, this application will read a file that contains user list data (can be replaced with a table in database in real implementation) and store the data in a variable as an array. Create a "Server.js" file, then insert this following code.
var http = require('http'), url = require('url'), sys = require('util'); var handler = require('./lib/Handler.js'); http.createServer(function(request, response) { try { console.log('Request from: ' + request.connection.remoteAddress + ' | href: ' + url.parse(request.url).href); handler.handle(request, response); } catch (e) { sys.puts(e); response.writeHead(500); response.end('Internal Server Error'); } }).listen(8087, "127.0.0.1", function () { console.log("Server running at http://localhost:8087/"); });
 
This article still use the same "Server.js" file with the previous article.
  This program will handle every request that received from clients with the handle function that located in Handler library.
handle(request, response)
  You also need to create "Handler.js" under directory 'lib'. This "Handler.js" file is different with the "Handler.js" file in the previous article. The differences are shown by the comments on them.
var fs = require('fs'), path = require('path'), // load the User class mUser = require('./../model/User'); // create the instance of User class var User = new mUser.User(); this.handle = function (request, response) { var renderHtml = function (content) { response.writeHead(200, { 'Content-Type': 'text/html' }); response.end(content, 'utf-8'); }; var notFound = function () { response.writeHead(404, { 'Content-Type': 'text/plain' }); response.end("404 Not Found\n"); }; // function that return JSON object var respJSON = function (response, object) { response.writeHead(200); response.end(JSON.stringify(object)); }; var parts = request.url.split('/'); var rest = parts.slice(2).join('/'); var page_rest = parts.slice(1).join('/'); if(request.url == '/') { fs.readFile('./web/index.html', function (error, content) { if(error) { notFound(); return; } else { renderHtml(content); } }); } // check if the requested URL started with 'action', it means that // the request is an ajax request else if(parts[1] == 'action') { // check whether the request method is a POST method if(request.method === 'POST') { // get the data in request body var temp = ''; request.on('data', function (chunk) { temp += chunk; }); request.on('end', function() { var data = {}; var action = parts[2]; data = JSON.parse(temp); console.log('action to \'' + action + '\', data: ' + JSON.stringify(data)); // if the action is login if(action == 'login') { // initialize 'resp' variable that will be used to be the response var resp = { success: false, message: '' }; // simple validation if(data.Username == '') { resp.message = 'Username must be filled'; } else if(data.Password == '') { resp.message = 'Password must be filled'; } else { // you can review the User class in the file 'model/User.js' // call the login function User.doLogin(data.Username, data.Password, function (userData) { // callback for success resp.result = true; }, function() { resp.message = 'Login failed'; }); } // return the resp variable as response respJSON(response, resp); } // you can add other additional actions here }); } else { notFound(); } } else if(parts[1] != 'css' && parts[1] != 'images' && parts[1] != 'js') { fs.readFile('./web/' + page_rest, function (error, content) { if(error) { notFound(); return; } else { renderHtml(content); } }); } else { path.exists('./web/' + parts[1] + '/' + rest, function(exists) { if(!exists) { notFound(); return; } fs.readFile('./web/' + parts[1] + '/' + rest, 'binary', function(error, content) { response.writeHead(200); response.end(content, 'binary'); }); }); } };
  The main difference is, this "Handle.js" has a new action for a requested URL like '/action/[something]'. This "Handle.js" will do action based on the [something] word. In the example above, "Handle.js" only handle the 'action' request if it's a POST-method request and the [something] word is 'login'. It also load a model object that comes from "User.js", this will be explained later.  

User Model

Create a "User.js" file under the 'model' directory. This object will load the User data file (in this example is a 'User.dat' file that located under the 'data' directory.
// thanks to jaeckel for this FileLineReader :) // http://blog.jaeckel.com/2010/03/i-tried-to-find-example-on-using-node.html var flr = require('./../lib/FileLineReader'); exports.User = function() { var data = {}; // initialize the data try { var reader = new flr.FileLineReader('./data/Users.dat'); // generate as hash table while(reader.hasNextLine()) { var line = reader.nextLine(); var res = line.split(';'); if(res.length!=3) return false; data[res[0]] = { Username: res[0], Password: res[1] }; } reader.close(); } catch (e) { console.log(e.toString()); return false; } this.doLogin = function(username, password, onSuccess, onFailure) { // check the hash table if(typeof(data[username])!=='undefined') { // if the password is right, call the onSuccess function if(data[username].Password == password) onSuccess(data[username]); // else call onFailure function else onFailure(); } else { onFailure(); } } return this; };
  This is the "User.dat" file I used under the 'data' directory.
testuser;password 1;
foouser;foo;
  I also want to say thanks to Jaeckel, I use his "FileLineReader" to read line by line my "User.dat" file. You can get the "FileLineReader" detail here.  

Login Page

I use a simple login page that send request using jquery ajax.
$(document).ready(function () { $('#LoginForm_btnSubmit').click(function (e) { $.ajax({ type: "POST", url: 'action/login', contentType: 'text/json', dataType: "json", data: JSON.stringify({ Username: $('#LoginForm_username').val(), Password: $('#LoginForm_password').val() }), success : function(result) { if(result.result) { document.location.href = 'main'; } else { $('#LoginForm_message').html(result.message); } }, error: function(jqXHR, textStatus, errorThrown) { alert("Internal Server Error"); } }); }); });
(I'm sorry that I can't show the full version of the "login.html" file because of some limitations of showing code in my blog)  

Conclusion

Using this concept of signing in, this application will only read the User data file (or database) once (in best performance where there is no data updated). Checking whether the data exists or not only need to check the hash table of the user data. But if the data of the users is too large, it will make the server uses more spaces in memory.