Authentication và middleware trong Laravel 7

Authentication trong Laravel 7

Authentication trong laravel 7 giúp bạn tạo nên chức năng đăng nhập, thoát, check login, logout, register…một cách dễ dàng.

Khai báo các thông số authentication trong file config/auth.php.

Thực hiện authentication tức phải tương tác với bảng users. Do đó Laravel đã tạo sẵn model User trong file app\user.php cho bạn. Nếu bạn không dùng Eloquent thì có thể sử dụng query builder thay thế.

1.  Cài đặt

Để triển khai authentication trong Laravel, bạn cài đặt gói laravel/ui vào project:  

composer require laravel/ui

Sau đó chạy lệnh

php artisan ui vue --auth

Lệnh này sẽ :

  • Tạo các view trong folder views\auth : login.blade.php, register.blade.php …
  • Tạo file views\layouts\app.blade.php – là 1 file layout cơ bản với các class css dựa trên bootstrap nhưng bạn có thể sửa lại.
  • Tạo các đường route xử lý authentication trong file route/web.php
  • Tạo file Http\Controllers\HomeController.php
  • Tạo các controller trong folder Http\Controllers\Auth như RegisterController, LoginController, ForgotPasswordController, ResetPasswordController.

Nếu website không cần đăng ký thành viên thì bạn có thể xóa RegisterController và khai báo lệnh Auth::routes([‘register’ => false]);

Mặc định Laravel dùng email để authentication, nếu bạn muốn dùng username thì có thể định nghĩa hàm username trong LoginController.php như sau:

public function username() {
    return 'username';
}

Để thay đổi các thông tin user khi đăng ký, bạn có thể thực hiện trong RegisterController. Các quy tắc kiểm tra dữ liệu trong form đăng ký sẽ sửa trong hàm validator còn các thông tin cần lưu vào db sẽ sữa trong hàm create

protected function validator(array $data) {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
}
protected function create(array $data){
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
}

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

use Illuminate\Support\Facades\Auth;
$user = Auth::user(); // Lấy thông tin user đã đăng nhập
$id = Auth::id(); // Lấy id của user đã đăng nhập

Các khác là truy xuất trong đối tượng request.

Request;
class ProfileController extends Controller {
    public function update(Request $request)  {
        // $u = $request->user();  //trả về thông tin user đã login
    }
}

3. Xác định user đã login chưa

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

use Illuminate\Support\Facades\Auth;
if ( Auth::check() )  {
    // User đã đăng nhập
}

4. 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

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');
}

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

Khi user chưa đăng nhập, nó sẽ chuyển user tới đường route có tên login. Bạn có thể chỉnh trong hàm redirectTo trong file Http/Middleware/Authenticate.php :

protected function redirectTo($request) {
    return route('login');
}

6. Thoát

Chức năng thoát có thể thực hiện bằng hàm logout

use Illuminate\Support\Facades\Auth;
Auth::logout();

7.  Ví dụ

  • Chạy mirgate để tạo bảng users
php artisan migrate
  • Mở file database/seeds/DatabaseSeeder.php  và code thêm trong hàn run
DB::table('users')->insert([
      'name' => 'admin',
      'email' => 'teonv@gmail.com',
      'password' => bcrypt('hehe'),
]);
  • Chạy seeder để chèn user vào bảng
php artisan db:seed
  • Tạo route
Route::get('download', function(){
    return view("download");
});
  • Tạo resources/views/download.blade.php

Chào bạn !


Đây là trang download software, chỉ dành cho thành viên đăng nhập 
  • Test:  Nhập địa chỉ theo route đã tạo trên sẽ thấy view download
  • Thêm middleware() vào đường route để bảo vệ
Route::get('download', function(){
    return view("download");
})->middleware('auth');
  • Test: mở trình duyệt nhập địa chỉ theo route đã tạo sẽ thấy form login hiện ra => Đăng nhập vào với email và pass của 1 user đang có sẽ thấy view download hiện ra
  • Trong view download, chỉnh view download để được như sau:

Chào bạn Auth::user()->name?>

Đây là trang download software, chỉ dành cho thành viên đăng nhập 

Thóat

Xem lại thử sẽ thấy tên user login hiện ra

  • Tạo route thoat
Route::get('thoat', function(){
    Auth::logout();
    return redirect()->route('/');
});

Định nghĩa đường route có tên  /

Route::get('/', function () {
    return view('welcome');
})->name("/");
  • Test: Nhắp link Thoát sẽ thực hiện được

Middleware

Middleware là những class đặc biệt trong Laravel cung cấp cho bạn cách thức thực hiện các bộ lọc giữa request và response. Middeware có thể thực hiện filter trước và sau luồng xử lý nghiệp vụ chính.  Ví dụ: middleware kiểm tra người dùng đăng nhập, kiểm tra quyền, ghi log lỗi, log các hành động của user, thêm http header…

Một vài middleware đã có sẵn trong Laravel, như middlware auth, CSRF protection, được đặt sẵn trong folder app/Http/Middleware.

1. Tạo middleware

Để tạo mới một middleware, sử dụng lệnh make:middleware:

php artisan make:middleware checkAdmin

Lệnh này sẽ tạo ra class checkAdmin trong folder app/Http/Middleware. Chẳng hạn trong middleware này, bạn chỉ cho phép truy cập nếu giá trị idgroup là 1, nếu không sẽ chuyển request đến trang home.

namespace App\Http\Middleware;
use Closure;
class checkAdmin{
    public function handle($request, Closure $next) {
        if ($request->session()->get('idgroup')!= 1) {
            return redirect('home');
        }
        return $next($request);
    }
}

2. Middleware trước và middle sau

Việc middleware chạy trước hay chạy sau phụ thuộc vào nhu cầu nghiệp vụ. Ví dụ middleware sau sẽ làm một tác vụ trước khi request được xử lý:

namespace App\Http\Middleware;
use Closure;
class BeforeMiddleware{
    public function handle($request, Closure $next)    {
        // Perform action
        return $next($request);
    }
}

Middleware dưới đây sẽ thực hiện sau khi request được xử lý:

namespace App\Http\Middleware;
use Closure;
class AfterMiddleware{
    public function handle($request, Closure $next)    {
        $response = $next($request);
        // Perform action
        return $response;
    }
}

3. Đăng ký middleware

Global middleware

Nếu bạn muốn 1 middleware có thể thực thi trong mọi HTTP request, bạn chỉ cần thêm tên middleware vào phần $middleware của  Http/Kernel.php

    protected $middleware = [
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    ];

Middleware cho route

Nếu muốn gán middleware cho 1 route cụ thể, rồi thêm tên middelware vào phần $routeMiddleware.

    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
    ];

Khi middleware đã khai báo HTTP kernel, bạn có thể sử dụng hàm middleware gán cho 1 route:

Route::get('admin/profile', function () {
    //
})->middleware('auth');

  Bạn cũng có thể gán nhiều middleware cho một route:

Route::get('/', function () {
    //
})->middleware('first', 'second');

 Khi đã gán middleware, bạn cũng có thể sử dụng tên đầy đủ của middleware:

use App\Http\Middleware\checkAdmin;
Route::get('admin/profile', function () {
    //
})->middleware(checkAdmin::class);

4. Nhóm các middleware

Bạn có thể nhóm vài middleware lại trong một tên để gán vào route cho dễ hơn. Nếu vậy thì dùng khai báo trong dãy $middlewareGroups của kernel.php

Mặc định, Laravel cung cấp sẵn 2 nhóm middleware web và api chứa những middleware thường sử dụng mà bạn có thể muốn áp dụng cho web và API routes:

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
        'api' => [
            'throttle:60,1',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

Nhóm middleware được gán vào route và controller sử dụng cú pháp như với từng middleware:

Route::get('/', function () {
    //
})->middleware('web');
Route::group(['middleware' => ['web']], function () {
    //
});

5. Tham số middleware

Middleware cũng có thể nhận thêm các tham số truyền vào. Ví dụ, nếu ứng dụng của bạn cần xác thực có “role” cụ thể trước khi thực hiện một thao tác nào đó, bạn có thể tạo một CheckRole middleware để nhận tên của role như một tham số.

Bạn đặt các tham số bổ sung cho middleware ở sau tham số $next của hàm handle:

namespace App\Http\Middleware;
use Closure;
class CheckRole {
    public function handle($request, Closure $next, $role)  {
        if (! $request->user()->hasRole($role)) {
            // Redirect...
        }
        return $next($request);
    }
}

Tham số cho middleware được khai báo trên route bằng cách dùng dấu : giữa tên middleware và tham số , nhiều tham số thì cách nhau bởi dấy phẩy

Route::put('post/{id}', function ($id) {
    //
})->middleware('role:editor');

6. Terminable middleware

Đôi khi 1 middleware có thể cần thực hiện sau khi HTTP response đã được gửi xong cho trình duyệt. Ví dụ, “session” middleware đi kèm với Laravel cung cấp dữ liệu session sau khi response được gửi tới trình duyệt. Nếu bạn định nghĩa một hàm terminate vào trong middleware, nó sẽ tự động được gọi sau khi response được gửi tới trình duyệt.

namespace Illuminate\Session\Middleware;
use Closure;
class StartSession{
    public function handle($request, Closure $next) {
        return $next($request);
    }
    public function terminate($request, $response)  {
        // Store the session data...
    }
}

Hàm terminate sẽ nhận cả request và response. Khi đã định nghĩa terminable middleware, bạn phải thêm nó vào trong danh sách global middleware trong kernel.php