byjg / authuser
一个简单且可定制的类,用于在您的应用程序内部启用用户认证。它适用于XML文件、关系数据库和Moodle。
Requires
- php: >=7.4
- byjg/cache-engine: 4.9.*
- byjg/jwt-wrapper: 4.9.*
- byjg/micro-orm: 4.9.*
Requires (Dev)
- phpunit/phpunit: 5.7.*|7.4.*|^9.5
README
一个简单且可定制的类,用于在您的应用程序内部启用用户认证。它适用于XML文件、关系数据库和Moodle。
主要目的是处理验证用户、添加属性和创建访问令牌的所有复杂性,同时抽象数据库层。此类可以在请求之间将用户数据持久化到会话(或文件、memcache等)。
创建用户处理类
使用文件系统(XML)作为用户存储
<?php $users = new UsersAnyDataset('/tmp/pass.anydata.xml');
使用数据库作为用户存储
<?php $users = new ByJG\Authenticate\UsersDBDataset( 'connection', // The connection string. Please refer to the project byjg/anydataset new UserDefinition(), // The field metadata for store the users new UserPropertiesDefinition() // The field metadata for store the extra properties );
注意:请参阅Anydataset项目,了解可用的数据库和连接字符串。
使用Moodle作为用户存储
<?php $users = new UsersMoodleDataset('connection');
使用您的用户名和密码验证用户,并将结果持久化到会话中
<?php $user = $users->isValidUser('someuser', '12345'); if (!is_null($user)) { $userId = $user->getUserid(); $sessionContext = new \ByJG\Authenticate\SessionContext(\ByJG\Cache\Factory::createSessionPool()); $sessionContext->registerLogin($userId); }
检查用户是否之前已认证
<?php $sessionContext = new \ByJG\Authenticate\SessionContext(\ByJG\Cache\Factory::createSessionPool()); // Check if the user is authenticated if ($sessionContext->isAuthenticated()) { // Get the userId of the authenticated users $userId = $sessionContext->userInfo(); // Get the user and your name $user = $users->getById($userId); echo "Hello: " . $user->getName(); }
将额外信息保存到用户会话中
您可以在会话中保存数据,该数据仅在使用者登录期间存在。一旦用户注销,与用户会话存储的数据将被释放。
为当前用户会话存储数据
<?php $sessionContext = new \ByJG\Authenticate\SessionContext(\ByJG\Cache\Factory::createSessionPool()); $sessionContext->setSessionData('key', 'value');
从当前用户会话获取数据
<?php $sessionContext = new \ByJG\Authenticate\SessionContext(\ByJG\Cache\Factory::createSessionPool()); $value = $sessionContext->getSessionData('key');
注意:如果用户未登录,将抛出错误。
向用户添加自定义属性
<?php $user = $users->getById($userId); $user->setField('somefield', 'somevalue'); $users->save();
从会话注销
<?php $sessionContext->registerLogout();
关于SessionContext的重要说明
SessionContext
对象将存储有关当前上下文的信息。由于SessionContext使用PSR-6中定义的CachePool接口,您可以设置任何存储来保存会话上下文。
在我们的示例中,我们使用常规PHP会话来存储用户上下文(Factory::createSessionPool()
)。但如果你使用其他存储如MemCached,你必须为此会话定义一个唯一的前缀。注意,如果两个用户有相同的前缀,你可能会得到SessionContext的意外结果。
memcached示例
<?php $sessionContext = new \ByJG\Authenticate\SessionContext(\ByJG\Cache\Factory::createMemcachedPool(), 'UNIQUEPREFIX');
如果您不知道如何创建/管理该唯一前缀,请首选使用常规的Session对象。
架构
┌───────────────────┐
│ SessionContext │
└───────────────────┘
│
┌────────────────────────┐ ┌────────────────────────┐
│ UserDefinition │─ ─ ┐ │ ─ ─ ┤ UserModel │
└────────────────────────┘ ┌───────────────────┐ │ └────────────────────────┘
┌────────────────────────┐ └────│ UsersInterface │────┐ ┌────────────────────────┐
│ UserPropertyDefinition │─ ─ ┘ └───────────────────┘ ─ ─ ┤ UserPropertyModel │
└────────────────────────┘ ▲ └────────────────────────┘
│
┌────────────────────────┼─────────────────────────┐
│ │ │
│ │ │
│ │ │
┌───────────────────┐ ┌───────────────────┐ ┌────────────────────┐
│ UsersAnyDataset │ │ UsersDBDataset │ │ UsersMoodleDataset │
└───────────────────┘ └───────────────────┘ └────────────────────┘
- UserInterface包含具体实现的接口
- UsersDBDataset是实现,用于在数据库中检索/保存用户
- UserAnyDataset是实现,用于在XML文件中检索/保存用户
- UsersMoodleDatabase是实现,用于在Moodle数据库结构中检索用户
- UserModel是获取/设置用户的基本模型
- UserPropertyModel是获取/设置额外用户属性的基本模型
- UserDefinition将模型映射到数据库
数据库
通过UsersDBDataset类存储用户数据的默认结构如下
create table users ( userid integer AUTO_INCREMENT not null, name varchar(50), email varchar(120), username varchar(15) not null, password char(40) not null, created datetime, admin enum('Y','N'), constraint pk_users primary key (userid) ) ENGINE=InnoDB; create table users_property ( customid integer AUTO_INCREMENT not null, name varchar(20), value varchar(100), userid integer not null, constraint pk_custom primary key (customid), constraint fk_custom_user foreign key (userid) references users (userid) ) ENGINE=InnoDB;
使用上述数据库结构,您可以创建UsersDBDatase如下
<?php $users = new ByJG\Authenticate\UsersDBDataset( 'connection', new \ByJG\Authenticate\Definition\UserDefinition(), new \ByJG\Authenticate\Definition\UserPropertiesDefinition() );
自定义数据库
如果您有一个现有的数据库,其中包含上述所有字段但具有不同的名称,则可以使用UserDefinition和UserPropertiesDefinition类来自定义此信息。
<?php $userDefinition = new \ByJG\Authenticate\Definition\UserDefinition( 'users', // $table \ByJG\Authenticate\Model\UserModel::class, // Model class \ByJG\Authenticate\Definition\UserDefinition::LOGIN_IS_EMAIL, [ UserDefinition::FIELD_USERID => 'fieldname of userid', UserDefinition::FIELD_NAME => 'fieldname of name', UserDefinition::FIELD_EMAIL => 'fieldname of email', UserDefinition::FIELD_USERNAME => 'fieldname of username', UserDefinition::FIELD_PASSWORD => 'fieldname of password', UserDefinition::FIELD_CREATED => 'fieldname of created', UserDefinition::FIELD_ADMIN => 'fieldname of admin' ] );
添加自定义读取和更新修改器
<?php $userDefinition = new \ByJG\Authenticate\Definition\UserDefinition( 'users', // $table \ByJG\Authenticate\Model\User::class, \ByJG\Authenticate\Definition\UserDefinition::LOGIN_IS_EMAIL ); // Defines a custom function to be applied BEFORE update/insert the field UserDefinition::FIELD_PASSWORD // $value --> the current value to be updated // $instance -> The array with all other fields; $userDefinition->defineClosureForUpdate(UserDefinition::FIELD_PASSWORD, function ($value, $instance) { return strtoupper(sha1($value)); }); // Defines a custom function to be applied After the field UserDefinition::FIELD_CREATED is read but before // the user get the result // $value --> the current value retrieved from database // $instance -> The array with all other fields; $userDefinition->defineClosureForSelect(UserDefinition::FIELD_CREATED, function ($value, $instance) { return date('Y', $value); }); // If you want make the field READONLY just do it: $userDefinition->markPropertyAsReadOnly(UserDefinition::FIELD_CREATED);
扩展UserModel
可以扩展UserModel表,因为您创建了一个新的类,该类从UserModel扩展,以添加新字段。
例如,假设您的表有一个名为"otherfield"的字段。
您需要按以下方式扩展
<?php /** * This class is your model * This need to support the basic field plus your new fields * already set in your definition class */ class MyUserModel extends UserModel { protected $otherfield; public function __construct($name = "", $email = "", $username = "", $password = "", $admin = "no", $field = "") { parent::__construct($name, $email, $username, $password, $admin); $this->setOtherfield($field); } public function getOtherfield() { return $this->otherfield; } public function setOtherfield($otherfield) { $this->otherfield = $otherfield; } }
然后您可以使用您的新定义
<?php $users = new ByJG\Authenticate\UsersDBDataset( 'connection', new \ByJG\Authenticate\Definition\UserDefinition( 'tablename', MyUserModel::class, UserDefinition::LOGIN_IS_EMAIL ), new \ByJG\Authenticate\Definition\UserPropertiesDefinition() );
安装
只需键入
composer require "byjg/authuser"
运行测试
因为此项目使用PHP会话,所以您需要以下方式运行单元测试
./vendor/bin/phpunit --stderr