老杨说他的用户信息存在 Redis 里面
去找老杨,他说要请我吃饭,结果到那一看,他正咬着笔目光呆滞,“怎么了?”我问他。
“你来得正好,帮我看下这个登录怎么弄。“
”一个登录,还能怎么弄,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 协议》,转载必须注明作者和本文链接
:bowtie: 搬好板凳来学习
我靠,我居然看完了。
点赞点赞。
对了,你听说过沙县国际吗?: :) :
简直及时雨,我刚想着怎么做,教程就来了,哈哈!
很幽默,学习了。
厉害了
手动点赞 :thumbsup:
这算是沙县被黑的最惨的一次么?
我跳过代码,把对话看完了。对话很吸引我。一会儿去沙县来个大肉饭!
对话很赞
方案是否可行?还是黑幽默?我感觉直接mySQL就行了。干嘛用redis缓存啊(^^)
好有才的对话~赞(((o(゚▽゚)o)))
?? 还是 ?:
@我是谁 php7新语法,http://php.net/manual/zh/migration70.new-f...
这个情景好,虽然代码没看懂。
蛮有意思。
非常有意思
对话太多,还是秒看代码
@我是谁
?:
判断左边是否为TRUE
,??
判断左边是否为NULL
挺好的,有时间跟着做
啥意思?
受益匪浅,感谢分享~!
有个疑惑,根据ID获取的时候 key 在 Redis 里面是不存在的 , 这个是有什么其他处理的代码吗?
我现在的处理方式是把username作为ID。
再次感谢。
赞!思路清晰,过了两遍。哈!
沙县给你多少钱,我河间驴肉火烧给你翻倍
挺有意思的,求后续