去找老楊,他說要請我吃飯,結果到那一看,他正咬著筆目光呆滯,“怎麼了?”我問他。
“你來得正好,幫我看下這個登入怎麼弄。“
”一個登入,還能怎麼弄,php artisan make:auth ,然後 php artisan migrate 建好表結構不就行了,登入註冊的頁面都好了,連路由都好了。“
”不一樣,這個使用者的資訊不是放在關係型資料庫裡,放在 Redis 裡。“
”幹嘛放 Redis 裡?“
”說是放 Redis 裡速度快,連 Redis 快,查也快。登入頁我都套好了,接下來怎麼搞?“
”不就加個自定義使用者提供者嘛,至於把你折騰成那樣?“
”you can you up !“
”滾“
拉過鍵盤,開啟命令列,熟悉地進行專案目錄,先來個
php artisan make:auth
以建立相應的框架,接下來正想 php artisan migrate
發現不對勁。要用 Redis 哦。
沒事,我們建立一個新的使用者提供者,就叫 RedisUserProvider 放在 app/Providers/ 下面。要實現 UserProvider 。
空的長這樣
<?php
namespace App\Providers;
use Illuminate\Contracts\Auth\UserProvider;
class RedisUserProvider implements UserProvider
{
}
要實現下列方法
public function retrieveById($identifier); // 根據 $identifier 一般是 ID 來取回使用者資訊
public function retrieveByToken($identifier, $token); // 根據 $identifier 和 $token 來取回使用者資訊
public function updateRememberToken(Authenticatable $user, $token); // 登入後儲存使用者的 token
public function retrieveByCredentials(array $credentials); // 根據 $credentials 就象使用者名稱密碼之類的,取回使用者資訊
public function validateCredentials(Authenticatable $user, array $credentials); // 根據 $credentials 就象使用者名稱密碼之類的,檢查使用者名稱密碼是否正確。
要返回的使用者資訊可以是一個陣列,給 Illuminate\Auth\GenericUser 轉一下就可以用了。
所以,先寫第一個方法,陣列資訊轉 user .
/**
* Get the generic user.
*
* @param mixed $user
* @return \Illuminate\Auth\GenericUser|null
*/
protected function getGenericUser($user)
{
if (! is_null($user)) {
return new GenericUser((array) $user);
}
}
然後依次實現各個方法,首先,既然要求速度,我們就直接用 Redis 的 PHP 擴充套件,而不只是使用 predis 之流,當然不是說他不好。
定義一個 conn 並連線上。順便把快取的 prefix 取出來。
class RedisUserProvider implements UserProvider
{
protected $conn = null;
protected $prefix = '';
public function __construct($config)
{
$this->prefix = config('cache.prefix');
$this->conn = new \Redis();
$this->conn->connect(config('database.redis.default.host','127.0.0.1'),config('database.redis.default.port',6379));
$this->conn->auth(config('database.redis.default.password',null));
}
使用者名稱列到底叫什麼名字呢? name ? username ? user_name ? account ? 算了,定義一個配置項,順便密碼也定義個算了。從 $config 傳過來。於是。
class RedisUserProvider implements UserProvider
{
protected $conn = null;
protected $prefix = '';
protected $username_field='';
protected $password_field='';
public function __construct($config)
{
$this->username_field = $config['username_field']??'username';
$this->password_field = $config['password_field']??'password';
$this->prefix = config('cache.prefix');
$this->conn = new \Redis();
$this->conn->connect(config('database.redis.default.host','127.0.0.1'),config('database.redis.default.port',6379));
$this->conn->auth(config('database.redis.default.password',null));
}
我們把使用者的資訊呀,token呀之類的存在Redis,相應 key 怎麼定義呢?也定義幾個吧。從 $config 傳過來。於是。
class RedisUserProvider implements UserProvider
{
protected $conn = null;
protected $prefix = '';
protected $username_field='';
protected $password_field='';
protected $key_user_id = '';
protected $key_user_name= '';
protected $key_user_token = '';
public function __construct($config)
{
$this->username_field = $config['username_field']??'username';
$this->password_field = $config['password_field']??'password';
$this->prefix = config('cache.prefix');
$this->key_user_id = $this->prefix.':'.($config['key_user_id']??'USER_ID').':';
$this->key_user_name = $this->prefix.':'.($config['key_user_name']??'USER_NAME').':';
$this->key_user_token = $this->prefix.':'.($config['key_user_token']??'USER_TOKEN').':';
$this->conn = new \Redis();
$this->conn->connect(config('database.redis.default.host','127.0.0.1'),config('database.redis.default.port',6379));
$this->conn->auth(config('database.redis.default.password',null));
}
接下來可以開心地實現幾個方法了。全文如下:
<?php
/**
* User: ufo
* Date: 17/4/26
* Time: 下午8:44
*/
namespace App\Providers;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Auth\GenericUser;
class RedisUserProvider implements UserProvider
{
protected $conn = null;
protected $prefix = '';
protected $username_field='';
protected $password_field='';
protected $key_user_id = '';
protected $key_user_name= '';
protected $key_user_token = '';
public function __construct($config)
{
$this->username_field = $config['username_field']??'username';
$this->password_field = $config['password_field']??'password';
$this->prefix = config('cache.prefix');
$this->key_user_id = $this->prefix.':'.($config['key_user_id']??'USER_ID').':';
$this->key_user_name = $this->prefix.':'.($config['key_user_name']??'USER_NAME').':';
$this->key_user_token = $this->prefix.':'.($config['key_user_token']??'USER_TOKEN').':';
$this->conn = new \Redis();
$this->conn->connect(config('database.redis.default.host','127.0.0.1'),config('database.redis.default.port',6379));
$this->conn->auth(config('database.redis.default.password',null));
}
/**
* 根據 identifier 取回使用者資訊
* Retrieve a user by their unique identifier.
*
* @param mixed $identifier
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
return $this->getGenericUser($this->conn->hGetAll($this->key_user_id.$identifier));
}
/**
* 根據 identifier 和 rember token 取回使用者資訊
* Retrieve a user by their unique identifier and "remember me" token.
*
* @param mixed $identifier
* @param string $token
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
$key = $this->key_user_token.$identifier;
if ($this->conn->exists($key) && $this->conn->get($key) == $token) {
return $this->retrieveById($identifier);
} else {
return null;
}
}
/**
* 為使用者更新 remember token ,為空則刪除之
* Update the "remember me" token for the given user in storage.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param string $token
* @return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
$identifier = $user->getAuthIdentifier();
$key = $this->key_user_token.$identifier;
if (!$token) {
$this->conn->delete($key);
} else {
$this->conn->set($key,$token);
}
return true;
}
/**
* 按照給定的憑據(一般是使用者名稱密碼)檢索使用者
*
* Retrieve a user by the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
$key = $this->key_user_name.($credentials[$this->username_field]);
if ($this->conn->exists($key)) {
$user = $this->conn->hGetAll($key);
return $this->getGenericUser($user);
} else {
return null;
}
}
/**
* 按給定的憑據(一般是使用者名稱密碼)檢驗使用者是否可以登入
* Validate a user against the given credentials.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param array $credentials
* @return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
return password_verify($credentials[$this->password_field],$user->getAuthPassword());
}
/**
*
* Get the generic user.
*
* @param mixed $user
* @return \Illuminate\Auth\GenericUser|null
*/
protected function getGenericUser($user)
{
if (! is_null($user)) {
return new GenericUser((array) $user);
}
}
}
東西有了,要怎麼用呢?在 app/Providers/AuthServiceProvider.php
註冊下
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
Auth::provider('redis',function($app,array $config){
return new RedisUserProvider($config);
});
}
}
記得我們剛才一堆的配置嗎?
到 config/auth.php 裡配置,在 providers 節裡,新增一個 redis 節點。
配置成如下:
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'redis' => [
'driver' => 'redis',
'model' => '',
'username_field' => 'email',
'password_field' => 'password',
'key_user_id' => env('KEY_USER_ID','USER_ID'),
'key_user_name' => env('KEY_USER_NAME','USER_NAME'),
'key_user_token' => env('KEY_USER_TOKEN','USER_TOKEN'),
],
],
現在好了,可以用了。
老楊興奮地搶過鍵盤,熟練地開啟瀏覽器,切到登入頁,填好使用者名稱和密碼。只是他遲遲沒有點下登入,而是點了一根菸。
“我們剛連的 Redis 是個空庫,裡面什麼東西都沒有!“
”沒有?那還登入個P呀。“
”使用者資訊由另一個使用者API服務提供,我這只是快取,快取明白不?“
“有點餓了,我弄個帳號讓你能先登入吧。“
接過鍵盤,開啟 redis-cli ,然後pia pia 輸入以下指令
HSET "Laravel:USER_NAME:fakename_qufo@163.com" "id" "1"
HSET "Laravel:USER_NAME:fakename_qufo@163.com" "email" "fakename_qufo@163.com"
HSET "Laravel:USER_NAME:fakename_qufo@163.com" "password" "$2y$10$4Gu6Q/wa3Gx35yl7bqbeKuPnK9fxbxtGZNG.GNPfas8mUXYjVWNEy"
HSET "Laravel:USER_NAME:fakename_qufo@163.com" "name" "qufo"
HSET "Laravel:USER_NAME:fakename_qufo@163.com" "remember_token" ""
“好了,使用者名稱 fakename_qufo@163.com 密碼 123456 ,登入試試。“
“哎,那個 remember_token 是什麼,為什麼是空的?”
“那個呀,存 remember_token 的。“
”別騙我,不是有另一個,你還定義成 key_user_token 的,存 token 麼?“
“先別管,關公戰秦瓊聽過沒,叫你定來你就定,你若是不定,他不管飯。”
”不就是一頓飯嘛,至於麼,對了,你聽說過沙縣麼?“
。。。
本作品採用《CC 協議》,轉載必須註明作者和本文連結