打算写一个webman的auth组件,参考Yii2的user组件
$auth = new \Webman\Auth\Auth();
$auth->isGuset();
$auth->login();
$auth->logout();
功能没有问题 ,但是每次都要new一下,在框架多个地方调用就要new多次实在不方便,于是做了一个单例
<?php
namespace support;
class Auth
{
private static $instance;
public static function getInstance()
{
if( self::$instance == false ){
self::$instance = new \Webman\Auth\Auth();
}
return self::$instance;
}
public static function __callStatic($method, $args)
{
$instance = static::getInstance();
if (! $instance) {
throw new RuntimeException('未获取到 Webman\Auth\Auth 的实例');
}
return $instance->$method(...$args);
}
}
一开始测试好像没啥问题
Auth::login()
Auth::isGuest()
Auth::user()->email
...
结果头痛的地方来了,使用support\Auth::method()这种方法调用时,每次我登录又退出后,就发现登录状态还在 session是删除了的,贴一下代码
<?php
namespace Webman\Auth;
//implements
class Auth
{
//登录地址
public $login_url = '';
public $return_url = '';
public $session_id = '';
public $identityClass = '';
public $_identity;
public function __construct($guard='web')
{
$config = config("auth.guards.{$guard}");
if( $config && is_array($config) ){
foreach ($config as $key => $value) {
$this->$key = $value;
}
}
}
/**
* 指定看守器
*/
public function guard(string $guard)
{
return (new Auth($guard));
}
/**
* 登录
*/
public function login($user)
{
$user_id = $user->id();
session([
$this->session_id => $user_id
]);
}
/**
* 退出
*/
public function logout()
{
return request()->session()->forget( $this->session_id );
}
/**
* 是否为游客
* @return 布尔值
*/
public function IsGuest()
{
return $this->user() === null;
}
/**
* 强制登录 否则跳转至登录页面
* webman中无法使用redirect
*/
public function loginRequired()
{
if( $this->IsGuest() ){
return redirect('/user');
}
}
public function redirectLogin()
{
if( $this->IsGuest() ){
return redirect($this->login_url);
}
}
/**
* 返回用户ID
*/
public function id()
{
if( $this->_identity == false ){
$this->renewAuthStatus();
}
return $this->_identity->id();
}
/**
* 获取用户类实例
*/
public function user()
{
if( $this->_identity == false ){
$this->renewAuthStatus();
}
if( $this->_identity){
//@114行 登录过一次后这里还能取到值
echo $this->_identity->nickname;
}
return $this->_identity;
}
public function renewAuthStatus()
{
$id = session($this->session_id);
//读取用户信息
if( $id == null ){
$identity = null;
}else{
$class = $this->identityClass;
$identity = $class::findIdentity($id);
}
//验证validateAuthKey
if ($identity !== null){
}
$this->setIdentity($identity);
}
public function setIdentity($identity)
{
if( $identity instanceof \Webman\Auth\IdentityInterface ){
$this->_identity = $identity;
}elseif ($identity === null){
//@149行 登录再退出 session删除了 这里把_identity设置为null 但是115行还能读取到之前的用户信息
echo "_identity set null";
$this->_identity = null;
}else{
throw new \Exception("identity must implements from IdentityInterface");
}
}
public function returnUrl()
{
return $this->return_url;
}
}
问题出在149和114行,149行明明把_identity设为null,代码也执行到了这里 114行的_identity却还是能取出用户数据
@walkor I need you
退出时重置 $this->_identity 也没用 下一个请求他还是有值
自问自答一下,问题已经解决,因为webman中单例和容器都是全局的,所以每次只在一次请求生命周期内有效的单例,直接放在request对象中即可,每次请求完毕webman都会帮你清理掉 也不会被其他用户取到这个变量
用户状态数据不适合放长生命周期对象里(不适合单例),比如有1万个在线用户,理论上内存中就要存储1万个用户的
_identity
状态,如果你只存一个,那就是所有用户共享的,只要有一个用户登录了,其它所有用户就共享这个状态了,导致数据错乱。另外webman是多进程的,进程A设置了某个用户的
_identity
状态,不会同步到进程B,如果这时候用户访问到了进程B,也会有问题。长生命周期的对象适合用在所有用户可以共享的类上,例如数据库对象、redis对象、配置对象、或者一些不带状态数据的处理类如Log类等。
webman中的session对象也是每个请求重新new一次,保证用户状态数据是从磁盘或者存储中拿到的最新数据。
不需要单例,多次new不影响什么,你这属于多虑