Laravel: Authentication (Phần 1)

Hệ thống xác thực Authentication của Laravel được xây dựng dựa trên 2 thành phần cốt lõi là: guards và providers. Tệp cấu hình xác thực của ứng dụng của bạn được đặt tại config/auth.php. Tệp này chứa một số tùy chọn được ghi chép đầy đủ để điều chỉnh hành vi của các dịch vụ xác thực của Laravel.

Laravel: Authentication (Phần 1)

Giới thiệu

Hệ thống xác thực Authentication của Laravel được xây dựng dựa trên 2 thành phần cốt lõi là: guardsproviders.

Tệp cấu hình xác thực của ứng dụng của bạn được đặt tại config/auth.php. Tệp này chứa một số tùy chọn được ghi chép đầy đủ để điều chỉnh hành vi của các dịch vụ xác thực của Laravel.

Dưới đây là ví dụ về một file auth.php:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Authentication Defaults
    |--------------------------------------------------------------------------
    |
    | This option controls the default authentication "guard" and password
    | reset options for your application. You may change these defaults
    | as required, but they're a perfect start for most applications.
    |
    */

    'defaults' => [
        'guard' => 'web',
        'passwords' => 'users',
    ],

    /*
    |--------------------------------------------------------------------------
    | Authentication Guards
    |--------------------------------------------------------------------------
    |
    | Next, you may define every authentication guard for your application.
    | Of course, a great default configuration has been defined for you
    | here which uses session storage and the Eloquent user provider.
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | Supported: "session", "token"
    |
    */

    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | User Providers
    |--------------------------------------------------------------------------
    |
    | All authentication drivers have a user provider. This defines how the
    | users are actually retrieved out of your database or other storage
    | mechanisms used by this application to persist your user's data.
    |
    | If you have multiple user tables or models you may configure multiple
    | sources which represent each model / table. These sources may then
    | be assigned to any extra authentication guards you have defined.
    |
    | Supported: "database", "eloquent"
    |
    */

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Resetting Passwords
    |--------------------------------------------------------------------------
    |
    | You may specify multiple password reset configurations if you have more
    | than one user table or model in the application and you want to have
    | separate password reset settings based on the specific user types.
    |
    | The expire time is the number of minutes that the reset token should be
    | considered valid. This security feature keeps tokens short-lived so
    | they have less time to be guessed. You may change this as needed.
    |
    */

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Password Confirmation Timeout
    |--------------------------------------------------------------------------
    |
    | Here you may define the amount of seconds before a password confirmation
    | times out and the user is prompted to re-enter their password via the
    | confirmation screen. By default, the timeout lasts for three hours.
    |
    */

    'password_timeout' => 10800,

];

Guards

Guard các bạn cứ hiểu như là một cách cung cấp logic được dùng để xác thực người dùng. Trong Laravel, thường hay dùng session guard hoặc token guard. Session guard duy trì trạng thái người dùng trong mỗi lần request bằng cookie. Còn Token guard xác thực người dùng bằng cách kiểm tra token hợp lệ trong mỗi lần request.

Vì vậy, như bạn thấy, guard xác định logic của việc xác thực, và không cần thiết để luôn xác thực bằng cách lấy các thông tin hợp lệ từ phía back-end. Bạn có thể triển khai một guard mà chỉ cần kiểm tra sự có mặt của một thông tin cụ thể trong headers của request và xác thực người dùng dựa trên điều đó.

'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
        ],
    ],

Providers

Nếu Guards hỗ trợ việc định nghĩa logic để xác thực thì Providers lấy ra dữ liệu người dùng từ phía back-end. Nếu guard yêu cầu người dùng phải hợp lệ với bộ lưu trữ ở back-end thì việc triển khai truy suất người dùng sẽ được providers thực hiện.Laravel hỗ trợ cho việc người dùng truy suất sử dụng Eloquent và Query Buider vào database. Tuy nhiên, chúng ta có thể thêm bất kì thay đổi vào . Ví dụ nhé, các bạn đặt model User trong namespace App nữa mà các bạn muốn đặt trong namespace App\Model thì chúng ta sẽ thay đỏi providers trong file app/auth.php như sau :

'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Model\User::class,
        ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

Theo mặc định, Laravel dùng model App\Models\User (Eloquent model) trong thư mục app/Models của bạn. Model này mặc định có thể được sử dụng với driver eloquents authentication để truy vấn dữ liệu. Nếu app của bạn không sử dụng Eloquent, bạn có thể sử dùng driver là database authentication để thực hiện truy vấn đến cơ sở dữ liệu.
Chúng ta đăng ký tài khoản với hệ thống thì trường password thì phải tối thiểu 60 ký tự và tối đa 255 ký tự nhé. Chúng ta cũng có thể đổi thay đổi cho trường remember_tokennullable() và cột này có 100 ký tự, trường remember_token là trường sẽ lưu token để giành cho chức năng Remember me ở phần đăng nhập.

Tổng quan về cách hoạt động

Đầu tiên, hãy xem xét cách xác thực hoạt động. Khi sử dụng trình duyệt web, người dùng sẽ cung cấp username và password của họ thông qua biểu mẫu đăng nhập. Nếu những thông tin xác thực này là chính xác, ứng dụng sẽ lưu trữ thông tin về người dùng đã được xác thực trong session của người dùng. Một cookie có chứa session ID sẽ được gửi cho tình duyệt để các request tiếp theo ứng dụng web có thể liên kết với user qua session. Sau khi nhận được session từ cookie ở trình duyệt, ứng dụng sẽ lấy dữ liệu session dựa trên ID session, lưu ý rằng thông tin xác thực đã được lưu trữ trong session và sẽ coi người dùng là "đã được xác thực".

Khi một dịch vụ từ xa cần xác thực để truy cập API, cookie thường không được sử dụng để xác thực vì không có trình duyệt web. Thay vào đó, dịch vụ từ xa sẽ gửi API token đến API theo mỗi request. Ứng dụng có thể xác thực token được gửi đến dựa trên bảng API tokens hợp lệ và "xác thực" yêu cầu đang được thực hiện bởi người dùng được liên kết với API token đó.

Cài đặt

Ở bài này mình sử dụng auth với giao diện là boootstrap.

composer require laravel/ui
php artisan ui bootstrap --auth
npm install
npm run dev
php artisan migrate
php artisan serve

Lấy thông tin của user đang đăng nhập

use Illuminate\Support\Facades\Auth;

// lấy thông tin của user hiện tại
$user = Auth::user();

// lấy id của user hiện tại
$id = Auth::id();

Kiểm tra xem người dùng đã đăng nhập chưa

Để xác định user đã login chưa, bạn dùng hàm check(), hàm này trả về true nếu đã login:

use Illuminate\Support\Facades\Auth;

if (Auth::check()) {
    // The user is logged in...
}

Lưu ý: mặc dù hàm này có thể check xem người udngf đã đăng nhập hay chưa nhưng thực tế ta nên dùng middleware để xác thực người dùng trước khi cho phép truy cập vào các route và controller nhất định

Hạn chế truy cập

Bạn có thể bảo vệ các route chỉ để phục vụ cho các user đã đăng nhập bằng hàm middleware auth

Route::get('profile', function () {
    // Chỉ những user đã đăng nhập mới được vào
})->middleware('auth');

Trong controller, bạn có thể gọi hàm middleware trong __constructor của controller thay vì gắn vào route như trên

public function __construct() {    
	$this->middleware('auth');
}

Redirect khi user chưa đăng nhập

Khi middleware auth phát hiện user chưa đăng nhập nó sẽ chuyển user đến  route có tên là login

/**
 * Get the path the user should be redirected to.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return string
 */
protected function redirectTo($request)
{
    return route('login');
}

Đăng nhập thủ công

Trước hết ta cần thêm Auth facade vào phía trên của class

use Illuminate\Support\Facades\Auth;
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    /**
     * Handle an authentication attempt.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function login(Request $request)
    {
        $credentials = $request->validate([
            'email' => ['required', 'email'],
            'password' => ['required'],
        ]);

        if (Auth::attempt($credentials)) {
			return redirect()->route('home')
        }

        return back()->withErrors([
            'email' => 'The provided credentials do not match our records.',
        ]);
    }
}

Ở đoạn code trên thì method attempt sẽ nhận vào một mảng các cặp key / value làm đôi số đầu tiên của nó. Các value này trong mảng sẽ được sử dụng để tìm user trong bảng users trong cơ sở dữ liệu của bạn. Vì vậy, trong ví dụ trên, user sẽ được truy xuất theo giá trị của cột email. Nếu user được tìm thấy, password ở dạng hash được lưu trữ trong cơ sở dữ liệu sẽ được so sánh với password được truyền cho method attempt thông qua mảng. Bạn không nên băm password của request đến, vì laravel sẽ tự động băm password trước khi so sánh nó với password đã băm trong cơ sở dữ liệu. Một phiên đã xác thực sẽ được bắt đầu cho người dùng nếu hai password băm khớp nhau.

Hãy nhớ rằng, các dịch vụ xác thực của Laravel sẽ truy xuất user từ cơ sở dữ liệu của bạn dựa trên cấu hình "provider" của bạn. Trong tệp cấu hình config/auth.php mặc định, "provider" sử dụng Eloquent user và nó được hướng dẫn sử dụng model App\Models\User khi truy xuất user. Bạn có thể thay đổi các giá trị này trong tệp config của mình dựa trên nhu cầu của ứng dụng của bạn.

Phương thức attempt sẽ trả về true nếu xác thực thành công. Nếu không, false sẽ được trả về.

Thêm điều kiện khi đăng nhập

Nếu muốn, bạn cũng có thể thêm các điều kiện truy vấn bổ sung vào truy vấn xác thực ngoài email và mật khẩu của người dùng. Để thực hiện điều này, chúng ta có thể chỉ cần thêm các điều kiện truy vấn vào mảng được truyền cho phương thức try. Ví dụ: chúng tôi có thể xác minh rằng người dùng được đánh dấu là "active":

if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
    // Authentication was successful...
}

Remember User

Nhiều ứng dụng web cung cấp checkbox "remember me" trên biểu mẫu đăng nhập của họ. Nếu bạn muốn cung cấp chức năng "remember me" trong ứng dụng của mình, bạn có thể chuyển một giá trị boolean làm đối số thứ hai cho phương thức attempt. Khi giá trị này là true, Laravel sẽ giữ cho người dùng được xác thực vô thời hạn hoặc cho đến khi họ đăng xuất theo cách thủ công. Bảng người dùng của bạn phải bao gồm cột chuỗi remember_token, cột này sẽ được sử dụng để lưu trữ mã thông báo "nhớ tôi". Việc di chuyển bảng users đi kèm với các ứng dụng Laravel mới đã bao gồm cột này:

use Illuminate\Support\Facades\Auth;

if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
    // The user is being remembered...
}

Đăng xuất

Để đăng xuất người dùng khỏi ứng dụng của bạn theo cách thủ công, bạn có thể sử dụng phương pháp đăng xuất do Auth facade cung cấp. Thao tác này sẽ xóa thông tin xác thực khỏi phiên của người dùng để các yêu cầu tiếp theo không được xác thực. Ngoài việc gọi phương thức đăng xuất, bạn nên làm mất hiệu lực phiên của người dùng và tạo lại mã thông báo CSRF của họ. Sau khi đăng xuất người dùng, bạn thường sẽ chuyển hướng người dùng đến thư mục gốc của ứng dụng của bạn:

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

/**
 * Log the user out of the application.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return \Illuminate\Http\Response
 */
public function logout(Request $request)
{
    Auth::logout();

    $request->session()->invalidate();

    $request->session()->regenerateToken();

    return redirect('/');
}

Tham khảo

https://viblo.asia/p/tim-hieu-ve-authentication-trong-laravel-Ljy5VoG3Kra

https://laravel.com/docs/8.x/authentication

https://longnv.name.vn/lap-trinh-laravel/authentication-va-middleware-trong-laravel-7