Ngu ngơ học làm web (x30) - CakePHP2 - Auth, đăng nhập, đăng xuất

Tiếp theo của: Ngu ngơ học làm web (x29) - CakePHP2 – saveAll(), array->JSON
-----

Phần x30. CakePHP2 – Auth, đăng nhập, đăng xuất


Xem (clip số 32 – chickenrainshop):

Trong website, có một số trang ai cũng có thể truy cập vào, tuy nhiên có một số trang cần phải đăng nhập thì mới truy cập được. Để làm điều này, cần phải có cơ chế đăng nhập và xác thực người dùng.

Đọc thêm để phân biệt: identifying (anh tên gì?), authenticating (có đúng là anh không?) và authorizing (mời anh đọc báo?).

CakePHP sử dụng AuthComponent để thực hiện việc xác thực người dùng.

Quy trình xây dựng một chức năng bằng CakePHP:

Bước 1: Khởi tạo
Bước 2: Xử lý
Bước 3: Hiển thị
Một chức năng có thể cần một form để gửi dữ liệu về server, hoặc cần tạo một view, hoặc chỉ cần sử dụng link/postLink
Viết xử lý trong các function (hay còn gọi là action) của controller. Sử dụng Model, hàm find hoặc các component
Hiển thị lại dữ liệu do action trả về lên View (nếu cần), hoặc chỉ gửi thông báo lên View (sử dụng Flash)

Xem (clip số 33a – chickenrainshop):

Khai báo sử dụng authcomponent trong AppController.php

class AppController extends Controller {
            public $components = [
                        'Flash','Session','Tool',
                        'Auth' =>[
                                    'loginAction' => '/login',
                                    'authError' => 'Bạn cần phải đăng nhập để tiếp tục!',
                                    'flash' => [
                                                            'element' => 'error',
                                                            'key' => 'auth',
                                                            'params' => ['class' => 'alert alert-danger']
                                                ],
                                    'loginRedirect' => '/'
                                    ]
                        ];

Trong đó,

- loginAction: trỏ tới action xử lý cho việc đăng nhập, ví dụ: 'loginAction' => '/login'.

- authError: chuỗi thông báo khi người dùng chưa đăng nhập, nhưng lại muốn thực hiện một chức năng có yêu cầu đăng nhập, ví dụ: 'authError' => 'Bạn cần phải đăng nhập để tiếp tục!'. Bản chất của authError hoạt động giống như một thông báo bằng Flash. Có thể thiết lập thêm các tham số cho authError.

- flash: định dạng cho chuỗi thông báo ‘authError’

- loginRedirect: trỏ tới trang sẽ được hiển thị, nếu đăng nhập thành công

Cấu hình route cho việc đăng nhập:

[routes.php]

Router::connect('/', ['controller' => 'books', 'action' => 'index']);
Router::connect('/sach-moi', ['controller' => 'books', 'action' => 'latest_books']);
Router::connect('/login', ['controller' => 'users', 'action' => 'login']);

Tạo action login trong UsersController.php:

/**
 * login - đăng nhập
 */
            public function login() {
                        $this->set('title_for_layout', 'Đăng nhập');
                       
            }

Tạo view login.ctp trong View\Users,

<div class="panel panel-info">
            <h4 class="panel-heading"><span class="glyphicon glyphicon-user"></span></h4>
            <?php echo $this->Flash->render('auth'); ?>
            <?php echo $this->Form->create('User', ['class' => 'form-horizontal', 'inputDefaults' => ['label' => false]]); ?>
                        <div class="control-group">
                                    <label class="control-label" for="inputUsername">Username</label>
                                    <div class="controls">
                                                <?php echo $this->Form->input('username', ['placeholder' => 'Tên đăng nhập']); ?>
                                    </div>
                        </div>
                        <div class="control-group">
                                    <label class="control-label" for="inputPassword">Password</label>
                                    <div class="controls">
                                                <?php echo $this->Form->input('password', ['placeholder' => 'Mật khẩu']); ?>
                                    </div>
                        </div>
                        <div class="control-group">
                                    <div class="controls">
                                                <?php echo $this->Form->button('Đăng nhập', ['type' => 'submit', 'class' => 'col-lg-2 btn btn-primary']); ?>
                                    </div>
                        </div>
            <?php echo $this->Form->end(); ?>
</div>

Do ở đây đang sử dụng phiên bản CakePHP 2.9.6, khác so với trong clip, nên làm theo clip sẽ bị một số vấn đề:

- sql_dump: sẽ không xuất ra mật khẩu đã được băm (hashed)

- nhập mật khẩu ở dạng rõ (plain-text), giống như trong cơ sở dữ liệu đang có, sẽ không vượt qua được quá trình chứng thực


Đọc thêm một ví dụ mẫu về chứng thực (authenticating) và phân quyền (authorizing) tại đây: https://book.cakephp.org/2.0/en/tutorials-and-examples/blog-auth-example/auth.html

Vấn đề đang gặp phải là:

- Khi lưu mật khẩu vào cơ sở dữ liệu, mật khẩu chưa được băm (hashing), vẫn ở dạng rõ (plain text)

- Tại form login, sau khi người dùng nhập mật khẩu,  bấm nút Đăng nhập, PHP sẽ băm mật khẩu này 
và so sánh với mật khẩu trong cơ sở dữ liệu

- Kết quả là mật khẩu đã được băm sẽ không thể khớp với mật khẩu đang ở dạng rõ trong cơ sở dữ liệu

Cách khắc phục:

Thực hiện băm mật khẩu của người dùng và lưu lại vào cơ sở dữ liệu. Sử dụng hàm 
Security::hash($yourPassword, NULL, true) để băm.

Ví dụ,

Giả sử mật khẩu dạng rõ là ‘1’, thực hiện băm mật khẩu này (trong view login.ctp):

<div class="panel panel-info">
<?php echo 'hashedPasswords = '.Security::hash(1, NULL, true); ?>
            <h4 class="panel-heading"><span class="glyphicon glyphicon-user"></span></h4>

Kết quả băm là: 5059a8afd49865c1bf27881d9cfd5a22657ca1a9

Lưu kết quả băm này vào cơ sở dữ liệu, thay thế cho giá trị ‘1’ trước đó.

Bây giờ, kiểm tra lại bằng cách đăng nhập với mật khẩu ‘1’ là được.

Nội dung action login trong UsersController.php:

/**
 * login - đăng nhập
 */
            public function login() {
                        if ($this->request->is('post')) {
                                    if ($this->Auth->login()) { // thực thi quá trình chứng thực
                                                $this->redirect($this->Auth->redirect()); // nếu chứng thực thành công, chuyển tới trang được thiết lập trong biến Auth -> loginRedirect
                                    } else {
                                                $this->Flash->error('Sai tên đăng nhập hoặc mật khẩu', ['key' => 'auth', 'params' => ['class' => 'alert alert-danger']]);
                                    }
                        }
                        $this->set('title_for_layout', 'Đăng nhập');
                       
            }

Quá trình đăng xuất:

Sử dụng AuthComponent để logout, nó sẽ redirect về một action nào đó, mặc định AuthComponent sẽ redirect về view login.

Viết action logout trong UsersController.php:

/**
 * logout - đăng xuất
 */
            public function logout() {
                        $this->redirect($this->Auth->logout());
            }

Viết thêm đoạn mã để route (trong  routes.php):


Router::connect('/logout', ['controller' => 'users', 'action' => 'logout']);
-----------
Cập nhật 21/6/2017
-----------
Xem thêm:
Tổng hợp các bài viết về Ngu ngơ học làm web