-----
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 về chứng thực tại đây: https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html
Đọ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
-----------