byjg/authuser

一个简单且可定制的类,用于在您的应用程序内部启用用户认证。它适用于XML文件、关系数据库和Moodle。

4.9.1 2024-01-05 20:35 UTC

This package is auto-updated.

Last update: 2024-09-17 22:16:35 UTC


README

Build Status Opensource ByJG GitHub source GitHub license GitHub release

一个简单且可定制的类,用于在您的应用程序内部启用用户认证。它适用于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

依赖项

开源 ByJG