需求:
- 該功能基於原有的 users 表不作變動的情況下,讓一位使用者有多個權限,並限制某權限能否登入該系統
事前準備:
- 安裝好 Laravel 並且最少建立起能夠執行登入登出的功能
- 建立一張名為 role 的表,表內最少包含以下欄位
-
id(int)
-
user_id(int)
-
role(int)
開始:
-
從原有的 Users.php 拷貝一個 UsersModel.php (沿用原有的 Users 也可以)
-
建立一個 RoleModel.php
-
在 UsersModel.php 內加上以下方法
public function roles() { return $this->hasMany(RoleModel::class, 'user_id'); }
-
執行 php artisan make:provider CustomUserProvider 建立一個自己認證用的 Provider
-
這個 Provider 必需繼承 EloquentUserProvider 或 UserProvider。並且換掉 retrieveByCredentials() 函數的執行內容來進行驗證。
<?php namespace App\Providers; use App\Models\UsersModel; use Illuminate\Auth\EloquentUserProvider; use Illuminate\Support\Str; use Illuminate\Contracts\Support\Arrayable; /** * 自定義登入時驗證項目 * * @author LIN CHENGHUNG <k80092@hotmail.com> */ class CustomUserProvider extends EloquentUserProvider { /** * 透過驗證資訊來比對身份 * * @param array $credentials 驗證資訊 * * @return Illuminate\Contracts\Auth\Authenticatable */ public function retrieveByCredentials(array $credentials) { if ( empty($credentials) || (count($credentials) === 1 && Str::contains($this->firstCredentialKey($credentials), 'password')) ) { return; } $query = $this->newModelQuery(); // 追加條件 $query = $query->whereHas('roles', function ($query) { $query->whereRole(config('const.ROLE')['ADMIN']); }); foreach ($credentials as $key => $value) { if (Str::contains($key, 'password')) { continue; } if (is_array($value) || $value instanceof Arrayable) { $query->whereIn($key, $value); } else { $query->where($key, $value); } } return $query->first(); } }
-
修改 AuthServiceProvider.php 來將寫好的 Provider 註冊
Auth::provider('myAuthProvider', function ($app, array $config) { return new CustomUserProvider($app['hash'], 'App\Models\UsersModel'); });
-
修改 Auth.php 中預設的 eloquent provider 換成自己寫的 myAuthProvider
...... 'providers' => [ 'users' => [ 'driver' => 'myAuthProvider', // 修改這裡 'model' => App\Models\UsersModel::class, ], ......
基本到這裡為止就算沒有實作這個 Guard 也已經完成一個最簡單的限制特定權限登入的功能。不過如果需要針對認證流程再作定義的話,可以在實作一個 Guard 來完成!
-
建立一個管理員用的 Guard 叫做 AdminGuard(我放在 App\Guards 底下)
<?php namespace App\Guards; use Illuminate\Auth\SessionGuard; /** * 管理者守門員 * * @author LIN CHENGHUNG <k80092@hotmail.com> */ class AdminGuard extends SessionGuard { /** * 登入驗證 * * @param array $credentials * @param bool $remember * @return bool */ public function attempt(array $credentials = [], $remember = false) { // 自行定義驗證內容 return parent::attempt($credentials, $remember); } }
-
修改 AuthServiceProvider.php 來將寫好的 Guard 註冊
Auth::extend('admin', function ($app, $name, $config) { return new AdminGuard($name, new CustomUserProvider($app['hash'], 'App\Models\UsersModel'), $app['session.store']); });
-
增加 Auth.php 中的 guard
...... 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], // 增加這邊 'admin' => [ 'driver' => 'admin', 'provider' => 'users', ], ......
-
這樣就可以用以下的方式來進行登入
if (auth('admin')->attempt($credentials, $remember)) { // dosomething }
-
然後在像下面這樣設定路由的 guard 來限制哪些是管理者才能訪問的URL
Route::middleware('auth:admin')->get('test',function() { echo 'test'; });