Trong bài viết này mình sẽ hướng dẫn các bạn xây dựng chức năng đăng ký và đăng nhập sử dụng passport trong KrakenJS. Thực tế thì bạn có thể sử dụng tương tự những gì mình trình bày trong bài viết này nếu bạn sử dụng project khác không phải KrakenJS nhưng có sử dụng ExpressJS.

Chúng ta sẽ tiếp tục xây dựng trên blog mà chúng ta đã làm ở bài trước. Trong phần này chúng ta sẽ cài đặt thêm 4 module:


- passport // Hỗ trợ xác thực người dùng với các phương thức khác nhau.

- passport-local

- connect-flash // Sử dụng để tạo ra các flash messages.

- bcrypt-nodejs // Hỗ trợ mã hóa mật khẩu.

Cài đặt: Cd vào thư mục chứa project và tiến hành chạy lần lượt 4 lệnh sau:


npm install --save passport

npm install --save passport-local

npm install --save connect-flash

npm install --save bcrypt-nodejs

Tiếp theo trong file index.js ta require passportconnect-flash vào ứng dụng.


var passport = require('passport');
var flash = require('connect-flash');
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());

Tạo model user lưu trữ thông tin người dùng.

/models/user.js


'use strict';

var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');

// tạo cấu trúc db
var schema = mongoose.Schema({
       username: {type: 'String', required: true},
       email: {type: 'String', required: true},
       password: {type: 'String', default: null},
       avatar: {type: 'String', default: null}
});

// tạo ra mã hash
schema.methods.generateHash = function(password) {
 return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

// kiểm tra mật khẩu hợp lệ
schema.methods.validPassword = function(password) {
 return bcrypt.compareSync(password, this.password);
};

// tạo model cho user và export vào app
module.exports = mongoose.model('User', schema);

Trong các bài trước thì ta viết tất cả các route trong controllers/index.js, tuy nhiên việc này sẽ làm cho việc quản lý trở nên phức tạp khi nội dung file index.js ngày càng dài. Bây giờ ta sẽ tạo mới 1 controller có tên là auth.js chứa các route liên quan tới việc xác thực người dùng.


var User = require('../models/user');
var passport = require('passport');

module.exports = function (app) {
 app.get('/login', function(req, res) {
 // hiển thị view login và login message nếu nó tồn tại.
 res.render('login', { message: req.flash('loginMessage') });
 });

app.get('/signup', function(req, res) {

// hiển thị view login
 res.render('signup', { message: req.flash('signupMessage') });
 });

// route này phải đi qua 1 middleware kiểm tra trạng thái đăng nhập, 
//chỉ người dùng
// đã đăng nhập mới có thể vào trang này.
 app.get('/profile', isLoggedIn, function(req, res) {
 res.render('profile', {
 user : req.user 
 // lấy thông tin người dùng trong session 
 //và truyền qua view
 });
 });

// đăng xuất
 app.get('/logout', function(req, res) {
 req.logout();
 res.redirect('/login');
 });
};

// route middleware kiểm tra để chắc chắn là
// người dùng đã đăng nhập
function isLoggedIn(req, res, next) {

// nếu người dùng đã đăng nhập thì tiếp tục thực hiện
 if (req.isAuthenticated())
 return next();

// ngược lại điều hướng về đăng nhập.
 res.redirect('/login');
}

Import controller này vào controllers/index.js


var auth = require('./auth');

module.exports = function (router) {

....

auth(router);

....

}

Tiếp theo hãy tạo  view tương ứng với các thao tác đăng nhập và đăng ký.

public/templates/signup.dust


{>"layouts/master" /}

{<body}
<div class="col-xs-6 col-xs-offset-3">
 <h2 class="text-center">Đăng ký thành viên</h2>

{@if cond="{message.length} > 0"}
 <div class="alert alert-danger">{message}</div>
 {/if}
 <form action="/signup" method="post" class="form-horizontal">

<input type="hidden" name="_csrf" value="{_csrf}" />
 <div class="form-group">
 <label for="username">Tên truy cập: </label>
 <input type="text" class="form-control" name="username" id="username">
 </div>
 <div class="form-group">
 <label for="email">Email: </label>
 <input type="text" class="form-control" name="email" id="email">
 </div>
 <div class="form-group">
 <label for="password">Mật khẩu: </label>
 <input type="password" class="form-control" name="password" id="password">
 </div>
 <div class="form-group">
 <label for="repassword">Xác nhận mật khẩu: </label>
 <input type="password" class="form-control" name="repassword" id="repassword">
 </div>
 <div class="form-group">
 <button class="btn btn-primary" type="submit">Đăng ký</button>
 </div>
 </form>
 </div>
{/body}

Giao diện trông sẽ như thế này.

2015-05-12_200748

 

Tiếp theo cho public/templates/login.dust cũng tương tự nhưng ta sẽ bỏ bớt trường email và xác nhận mật khẩu.


{>"layouts/master" /}

{<body}
<div class="col-xs-6 col-xs-offset-3">
 <h2 class="text-center">Đăng nhập hệ thống</h2>

{@if cond="{message.length} > 0"}
 <div class="alert alert-danger">{message}</div>
 {/if}
 <form action="/login" method="post" class="form-horizontal">

<input type="hidden" name="_csrf" value="{_csrf}" />
 <div class="form-group">
 <label for="username">Tên truy cập: </label>
 <input type="text" class="form-control" name="username" id="username">
 </div>
 <div class="form-group">
 <label for="password">Mật khẩu: </label>
 <input type="password" class="form-control" name="password" id="password">
 </div>
 <div class="form-group">
 <button class="btn btn-primary" type="submit">Đăng nhập</button>
 </div>
 </form>
 </div>
{/body}

Thêm vào thư mục  library file passport.js với nội dung.


var LocalStrategy = require('passport-local').Strategy;

// load model User
var User = require('../models/user');

module.exports = function(passport) {

passport.serializeUser(function(user, done) {
 done(null, user.id);
 });

passport.deserializeUser(function(id, done) {
 User.findById(id, function(err, user) {
 done(err, user);
 });
 });

// =========================================================================
 // ĐĂNG NHẬP HỆ THỐNG ======================================================
 // =========================================================================
 passport.use('login', new LocalStrategy({
 // mặc định passport sử dụng username để đăng nhập. 
 // Bạn cũng có thể thay đổi thành email nếu muốn.
 usernameField : 'username',
 passwordField : 'password',
 passReqToCallback : true 
 // cho phép sử dụng route gọi đến để kiểm tra đăng nhập hay chưa
 },
 function(req, username, password, done) {

process.nextTick(function() {
 User.findOne({ 'username' : username }, function(err, user) {
 // nếu có lỗi thì trả về lỗi.
 if (err)
 return done(err);

if (!user || !user.validPassword(password))
 return done(null, false, req.flash('loginMessage',
  'Tên truy cập hoặc mật khẩu không chính xác'));

// thực hiện thành công thì trả về thông tin user
 else
 return done(null, user);
 });
 });

}));

// =========================================================================
 // ĐĂNG KÝ THÀNH VIÊN ======================================================
 // =========================================================================
 passport.use('signup', new LocalStrategy({
 usernameField : 'username',
 passwordField : 'password',
 passReqToCallback : true
 },
 function(req, username, password, done) {

process.nextTick(function() {

// kiểm tra xem username đã được sử dụng hay chưa
 User.findOne({'username': username}, function(err, existingUser) {

if (err)
 return done(err);

// nếu tồn tại user với username này rồi thì báo lỗi
 if (existingUser)
 return done(null, false, req.flash('signupMessage',
  'Tên truy cập đã tồn tại, vui lòng chọn tên khác.'));
 // Ngược lại thì tạo mới user
 else {

var newUser = new User();

newUser.username = req.body.username;
 newUser.email = email;
 newUser.password = newUser.generateHash(password);
 newUser.avatar='images/default.png';

newUser.save(function(err) {
 if (err)
 throw err;

return done(null, newUser, req.flash('signupMessage',
 'Đăng ký tài khoản thành công!'));
 });
 }

});
 });

}));

};

Require thư viện này vào trong controllers/auth.js: Thêm trên cùng nội dung file.

require('../library/passport')(passport);

Thêm xử lý khi nhấn nút đăng ký trong controllers/auth.js


app.post('/signup', passport.authenticate('signup', {
 successRedirect : '/profile', 
 // Chuyển về trang thông tin cá nhân nếu đăng ký thành công
 failureRedirect : '/signup', 
 // Điều hướng về lại trang đăng ký nếu có lỗi
 failureFlash : true // cho phép flash messages
 }));

Sau khi đăng ký thành công thì hệ thống sẽ điều hướng về trang thông tin cá nhân với thông tin của người dùng, hãy bổ sung thêm view profile.dust với nội dung:


{>"layouts/master" /}

{<body}
<div class="col-xs-6 col-xs-offset-3">
 <h2 class="text-center">Thông tin cá nhân</h2>
 <p class="text-center">
  <a href="/logout" class="btn btn-danger">
  Đăng xuất</a>
</p>
 <div class="jumbotron">
 <ul>
 <li>Tên truy cập: {user.username}</li>
 <li>Email: {user.email}</li>
 <li>Mật khẩu: {user.password}</li>
 <li>Ảnh đại diện: {user.avatar}</li>
 </ul>
 </div>
 </div>
{/body}

Kết quả:

2015-05-12_203959

 

Hãy nhấn nút đăng xuất và tiếp tục xây dựng chức năng đăng nhập nào.

Thêm route xử lý cho thao tác đăng nhập trong controllers/auth.js


app.post('/login', passport.authenticate('login', {
 successRedirect : '/profile', 
 // Chuyển về trang thông tin cá nhân nếu đăng ký thành công
 failureRedirect : '/login', 
 // Điều hướng về lại trang đăng nhập nếu có lỗi
 failureFlash : true // cho phép flash messages
 }));

Vậy là đã xong, hãy quay lại trang login và tận hưởng thành quả.

Lời kết:

Trong bài viết này mình đã hướng dẫn các bạn xây dựng hoàn chỉnh chức năng xác thực người dùng thông qua việc sử dụng thư viện passport. Ở bài viết tiếp theo mình sẽ hướng dẫn các bạn thực hiện xác thực người dùng qua tài khoản Facebook sử dụng passport-facebook.

 

 

Mình là 1 developer mới vào nghề, chưa có nhiều kinh nghiệm với lập trình web nhưng luôn muốn chia sẻ những hiểu biết của mình với các lập trình viên khác.
  • Địa chỉ: Hồ Chí Minh