[Laravel] Custom guard and auth provider
前言
如果要客製自己的 auth gard 用來處理驗證與登入首先可以從官網的 Authentication 中找到!
但是寫的有一點點不全面,在這邊重新整理一下。
如果是要用 jwt 登入這種,則可以直接參考官網即可!
大致需要
- 自己定義的 Auth Guard
- 自己定義的 User Provider (後面解釋)
- 用來驗證的 model
建立用來驗證的 model
<?php
namespace App\Models\Member;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Member extends Authenticatable
{
use SoftDeletes;
protected $table = 'members';
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
'cell_phone', 'verified', 'status',
'facebook_id', 'line_id',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
];
protected $dates = [
'deleted_at',
];
}
在這邊我主要是先建立一個 Member.php 的 model
這裡有些地方要注意,Member 這個 model 必須繼承 Authenticatable
才可以符合 Laravel 的規則,因為後面會使用注入 Authenticatable 的方式。
建立 UserProvier
什麼時候會需要自己建立 UserProvider 呢?假設今天使用的是 mongo db, 已經不是一般常用的 Relational DB
這時候就會需要建立一個客製化的 UserProvider
<?php
namespace App\Providers;
use App\Repositories\Member\MemberRepository;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Facades\Hash;
class MemberServiceProvider implements UserProvider
{
protected $memberRepo;
public function __construct(MemberRepository $memberRepo)
{
$this->memberRepo = $memberRepo;
}
/**
* Retrieve a user by their unique identifier.
*
* @param mixed $identifier
*
* @return null|Authenticatable
*/
public function retrieveById($identifier)
{
return $this->memberRepo->getById($identifier);
}
/**
* Retrieve a user by by their unique identifier and "remember me" token.
*
* @param mixed $identifier
* @param string $token
*
* @return null|Authenticatable
*/
public function retrieveByToken($identifier, $token)
{
return null;
}
/**
* Update the "remember me" token for the given user in storage.
*
* @param string $token
*/
public function updateRememberToken(Authenticatable $member, $token)
{
}
/**
* Retrieve a user by the given credentials.
*
* @param array $credentials array for auth
*
* @return null|Authenticatable
*/
public function retrieveByCredentials(array $credentials)
{
if (!empty($credentials['cell_phone'])) {
$member = $this->memberRepo->getByCellPhone($credentials['cell_phone']);
}
if (!empty($credentials['email'])) {
$member = $this->memberRepo->getByEmail($credentials['email']);
}
if (!$member) {
return null;
}
return Hash::check(
$credentials['password'],
$member->password
) ? $member : null;
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
// 驗證 email 與 password
if (isset($credentials['email'], $credentials['password'])) {
return strtolower($user->email) === strtolower($credentials['email'])
&& Hash::check($credentials['password'], $user->password);
}
// 驗證手機與 password
if (isset($credentials['cell_phone'], $credentials['password'])) {
return $user->cell_phone === trim($credentials['cell_phone'])
&& Hash::check($credentials['password'], $user->password);
}
return false;
}
}
UserProvider 有點像是用來取得用戶身份的一個媒介所以像我上面的範例,MemberServiceProvider 必須實作 UserProvider 這個 interface
我這裡是利用客製化的 Auth Guard (此處我命名為 MemberAuthGuard) 去呼叫 MemberServiceProvider,
來驗證前端傳遞過來的資訊是否正確,從而驗證用戶。
(MemberService - 呼叫 -> MemberAuthGuard - 呼叫 -> MemberServiceProvider)
建立 Auth Guard
<?php
namespace App\Services\Auth;
use App\Providers\MemberServiceProvider;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;
class MemberAuthGuard implements Guard
{
/**
* Undocumented variable.
*
* @var Illuminate\Contracts\Auth\UserProvider
*/
protected $provider;
/**
* member物件.
*
* @var Authenticatable
*/
protected $member;
public function __construct(MemberServiceProvider $provider)
{
$this->provider = $provider;
$this->member = null;
}
/**
* Determine if the current user is authenticated.
*
* @return bool
*/
public function check()
{
return isset($this->member);
}
/**
* Determine if the current user is a guest.
*
* @return bool
*/
public function guest()
{
return !$this->check();
}
/**
* Get the currently authenticated user.
*
* @return null|\Illuminate\Contracts\Auth\Authenticatable
*/
public function user()
{
return $this->member;
}
/**
* Get the ID for the currently authenticated user.
*
* @return null|int|string
*/
public function id()
{
return $this->member ? $this->member->id : null;
}
/**
* Validate a user's credentials.
*
* @return bool
*/
public function validate(array $credentials = [])
{
$member = $this->provider->retrieveByCredentials($credentials);
if (!$member) {
return false;
}
$this->setUser($member);
return true;
}
/**
* Set the current user.
*/
public function setUser(Authenticatable $member)
{
$this->member = $member;
}
}
因為必須實作 Guard ,就大概上面的 function 要實作而已。AuthServiceProvider 註冊自定義的 Provider 與 Auth Guard
<?php
namespace App\Providers;
use App\Repositories\Member\MemberRepository;
use App\Services\Auth\MemberAuthGuard;
use Illuminate\Auth\AuthManager;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*/
public function boot()
{
$this->registerPolicies();
$memberRepo = $this->app->make(MemberRepository::class);
// 自訂前端使用的驗證方式
Auth::provider('frontend-auth-provider', function ($app, array $config) use ($memberRepo) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
return new MemberServiceProvider($memberRepo);
});
// 定義 driver 的部分
Auth::extend('frontend-auth', function ($app, $name, array $config) {
return new MemberAuthGuard(Auth::createUserProvider($config['provider']));
});
$this->app->terminating(function () {
$this->app->singleton('auth', function ($app) {
$app['auth.loaded'] = true;
return new AuthManager($app);
});
});
}
}
在 Auth::provider('frontend-auth-provider') 這邊主要是註冊一個自定義的 User Provider
在 Auth::extend('frontend-auth') 是註冊 config/auth.php 裡面的 driver
參考:
https://codershandbook.com/implementing-custom-auth-guard-in-laravel
https://codershandbook.com/implementing-custom-auth-guard-in-laravel
https://laravel.com/docs/8.x/authentication#adding-custom-guards
留言
張貼留言