Session (Bài trước)
(Bài tiếp) PHP - Hello World!

Zend\Authentication là cách xác thực (ví dụ nhập user/password để kiểm tra) trong Zend Framework (Không phải là phân quyền). Ở đây nghiên cứ Authetication với ứng dụng xây dựng hệ thông đăng nhập (Login/Logout).

Zend Framework cung cấp lớp AuthenticationService nó sử dụng các loại Adapter xác thức và bộ nhớ lưu trữ dài hạn (ví dụ Database) để tạo ra dịch vụ Xác thực.

Cài đặt vào dự án bằng lệnh composer sau:

composer require zendframework/zend-authentication

Authetication Adapter hay dùng

Khi xác thực bạn cần dùng đến một Authetication Adapter để xác thực, trong ZF có các loại Adapter xác thực khác nhau (DbTable, Callback, Digest, Http, Ldap, CallbackCheckAdapter, CredentialTreatmentAdapter), chúng được mở rộng từ lớp AbstractAdapter nên cơ bản sử dụng chúng sẽ có dạng:

$authAdapter = /*khởi tạo theo tường loại*/

//Thiết lập Credential (pasword) và Identity (username|email|id ...)
$authAdapter->setIdentity($email);
$authAdapter->setCredential($password)

//Thực hiện việc xác thực
$result = $authAdapter->authenticate();

switch ($result->getCode()) {
    case Result::SUCCESS:
        //thành công
    break;
    default:
        //thất bại
    break;
}

Kết quả của việc xác thực được lưu trong $result, từ đó xác định được xác thực thành công hay không

Result

Đối tượng Result(Zend\Authentication\Result) là kết quả trả về từ các xác thực authenticate(). Nó có các phương thức:

getCode() Trả về mã xác thực:
  • FAILURE: 0 Xác thực thất bại
  • FAILURE_IDENTITY_NOT_FOUND: -1 identity không thấy
  • FAILURE_IDENTITY_AMBIGUOUS: -2identity không rõ ràng (ví dụ có 2 user cùng 1 email mà lại dùng email như là identity)
  • FAILURE_CREDENTIAL_INVALID: -3 credential sai (password sai)
  • FAILURE_UNCATEGORIZED: -4 lỗi (không phân loại được lỗi gì)
  • SUCCESS: 1 xác thực thành công
isValid() Trả về true xác thực thành công
getMessage() Lấy thông báo về kết quả xác thực
getIdentity() Lấy Identity (user ...)

CredentialTreatmentAdapter

CredentialTreatmentAdapter là loại Adapter xác thực mà nó thi hành một câu lệnh SQL để truy cấn bẳng dữ liệu dựa trên identity, credential cung cấp. Nếu SQL có trả về identity thì xác thực thành công. Thường sử dụng MD5() hoặc PASSWORD() để chuyển đổi credential

Giả sử bạn có bảng tên user, trong bảng có cột usernamepassword. Giờ bạn muốn tạo CredentialTreatmentAdapter xác thực bằng dữ liệu: username, password, có mấy trường hợp sau:

(1) Cột password không mã hóa gì

$authAdapter = new \Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter(
    $dbAdapter,     //Zend\Db\Adapter\Adapter
    'users',        //Tên bảng
    'username',     //Tên cột tương ứng với identity
    'password'      //Tên cột tương ứng với credential
    '?'             //Password không mã hóa
);

//Xác thực
$authAdapter->setIdentity('usernameabc');
$authAdapter->setCredential('passwordabc')

//Thực hiện việc xác thực
$result = $authAdapter->authenticate();

(2) Cột password mã hóa một lần MD5 hoặc PASSWORD

$authAdapter = new \Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter(
    $dbAdapter,     //Zend\Db\Adapter\Adapter
    'users',        //Tên bảng
    'username',     //Tên cột tương ứng với identity
    'password'      //Tên cột tương ứng với credential
    'MD5(?)'        //Hoặc PASSWORD(?)
);

CredentialTreatmentAdapter bản chất khi hoạt động nó tạo ra câu lệnh SQL, với ký hiểu ? đại diện cho dữ credential truyền vào bạn có thể tùy biến câu lệnh SQL phức tạp tùy ý

Ví dụ bảng user có thêm cột active bạn chỉ cho phép xác thực các user ở trạng thái active = true

$authAdapter = new \Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter(
    $dbAdapter,     //Zend\Db\Adapter\Adapter
    'users',        //Tên bảng
    'username',     //Tên cột tương ứng với identity
    'password'      //Tên cột tương ứng với credential
    'MD5(?) AND active = "TRUE"'
);

(3) Các tùy biến phức tạp

Bạn có thể lấy chính đối tượng Select để thiết lập: (xem Where)

$authAdapter = new \Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter(
    $dbAdapter,
    'users',
    'username',
    'password'
    'MD5(?)'
);

$select = $authAdapter->getDbSelect();
$select->where('active = "TRUE"');

Hoặc password có sử dụng đến salt (xem thêm Salt), với chuỗi salt lưu tại cột password_salt

$authAdapter = new \Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter(
    $dbAdapter,
    'users',
    'username',
    'password'
    'MD5(CONCAT(?, password_salt))'
    );

CallbackCheck

Adapter xác thực CallbackCheck cũng tạo câu lệnh SQL truy vấn đến CSDL như CredentialTreatmentAdapter, tuy nhiên nó lại dùng một hàm Callback để kiểm tra credential

Hàm callback phải có dạng:

//Trả về true thành công, false thất bại
//$hash là giá trị credential $authAdapter truyền vào lấy từ DB
//$password  là giá trị đưa vào kiểm tra
$passwordValidation = function ($hash, $password) {
    //...code kiểm tra theo cách của bạn
    return $result;
};

Ví dụ: sử dụng hàm callback với Password có sử dụng Bcrypt

$passwordValidation = function($dbCredential, $requestCredential) {
        $bcrypt =new \Zend\Crypt\Password\Bcrypt();
        $bcrypt->setCost(14);
        return $bcrypt->verify($requestCredential,$dbCredential);
    };

Tạo ra một Adapter: CallbackCheck như sau

$authAdapter = new AuthAdapter(
    $dbAdapter,
    'users',
    'username',
    'password',
    $passwordValidation
);

Hoặc

$authAdapter = new AuthAdapter(
    $dbAdapter,
    'users',
    'username',
    'password',
    function ($hash, $password) {
        //...code kiểm tra theo cách của bạn
        return $result;
    }
);

AuthenticationService

Khi sử dụng Adapter xác thực như trên thì sau khi xác thực thành công phải có cơ chế lưu trữ lại để trong cùng phiên truy cập không cần đăng nhập mỗi khi có Request mới gửi đến. Lức này bạn cần dùng tới AuthenticationService

AuthenticationService nó chứa bên trong một Adapter xác thực, một bộ lưu trữ dữ liệu cố định (ta sử dụng Zend\Authentication\Storage\Session để dùng $_SESSION lưu trữ thông tin đăng nhập)

Ví dụ tạo ra một AuthenticationService $auth:

//$authAdapter đã có, tạo theo cách ở phần trên

//Tạo lưu trữ bằng Session
$storageSession = new Zend\Authentication\Storage\Session('XTLAB')

$auth = new AuthenticationService($storageSession,$authAdapter);

Các phương thức của AuthenticationService

getAdapter() Lấy đối tượng Adapter xác thực
getStorage() Lấy Storage, nơi lưu để nhớ thông tin xác thực
hasIdentity() Kiểm tra có tồn tại Indentity (lưu thông tin xác thực)
getIdentity() Lấy Indentity (lưu thông tin xác thực)
clearIdentity() Xóa thông tin đăng nhập
authenticate() Tiến hành xác thực, trả về Result.
$auth->getStorage()->clear();

$auth
    ->getAdapter()
    ->setIdentity($Identity)
    ->setCredential($Credential);

$result = $auth->authenticate();

Đăng ký AuthenticationService là dịch vụ hệ thống

Nếu bạn đăng ký vào Service Manager của hệ thống một AuthenticationService, với key là : Zend\Authentication\AuthenticationService thì bạn đã có một dịch vụ xác thực mặc định.

Thường thiết lập trong config

//...
'service_manager' => [
    'factories' => [
        \Zend\Authentication\AuthenticationService::class
            =>  'factory_create_AuthenticationService'
    ]
//...
],

Nếu có đăng ký dịch vụ trên thì Plugin Controller Identity() và View Helper Identity() sẽ hoạt động, như vậy trong View, Controller bạn sẽ dễ dàng lấy được thông tin người dùng đăng nhập (hoặc không có đăng nhập thì Identity() trả về null)


Đăng ký nhận bài viết mới
Session (Bài trước)
(Bài tiếp) PHP - Hello World!