odevnet/dulceauth

PHP用户管理库,简化用户注册和认证,以及角色和权限的管理。它专为需要强大、高效和可扩展解决方案的小型和中型应用而设计。

v1.0.0 2024-08-19 18:13 UTC

This package is auto-updated.

Last update: 2024-09-30 18:49:37 UTC


README

GitHub tag (latest by date) GitHub Static Badge Static Badge

什么是“dulceAuth”?

DulceAuth是一个PHP用户管理库,简化用户注册和认证,以及角色和权限的管理。它专为需要强大、高效和可扩展解决方案的小到中型应用而设计。

一些特性包括

  • 您可以一次性注册一个或多个用户。
  • 通过分配角色和权限来控制对应用程序不同部分的操作。
  • 集成Laravel的"Eloquent ORM",以便方便地与数据库工作。
  • 通过生成和验证令牌来验证用户账户。
  • 重置密码。
  • 支持简单邮件发送。
  • 利用会话并允许验证用户会话。
  • 捕获并记录可能发生的任何错误。
  • 由于其模块化架构,易于扩展和自定义。
  • 通过使用“服务容器”来管理和简化依赖注入。

目录

  1. 安装和使用
    1. 通过GitHub
    2. 库使用
  2. 配置
    1. 数据库
    2. Config.php
  3. 异常
  4. Logger类
  5. 用户注册
    1. 账户验证
    2. 账户验证。第二部分。
    3. 一个特殊情况
  6. 登录
  7. 用户
    1. 它存在吗?
    2. 编辑用户
    3. 删除用户
    4. 创建用户
    5. 更改密码
    6. 恢复密码/忘记密码
  8. 角色和权限
    1. 创建角色
    2. 编辑角色
    3. 删除角色
    4. 将角色分配给用户
    5. 权限
      1. 创建权限
      2. 编辑权限
      3. 删除权限
      4. 将权限分配给角色
      5. 从角色中删除权限
  9. 角色和权限。第二部分
  10. 授权
  11. 会话
    1. 会话时长
  12. 邮箱类

以下是尊重Markdown格式的翻译

安装和使用

唯一的要求是

  • 至少需要MySQL数据库(其他数据库未测试)
  • PHP版本 >= 8.2.0

Github

在Windows上打开命令提示符(cmd)或操作系统终端,并导航到您想要克隆存储库的文件夹

例如,在控制台中输入

cd path/to/your/directory

然后在控制台中运行以克隆存储库

git clone https://github.com/odevnet/dulceAuth.git

一旦克隆完成,在控制台打开并在项目目录中,运行

composer install来自动安装必要的依赖项。

用法

一旦下载并配置了dulceAuth,只需包含并实例化库即可使用,如下所示

假设库在根目录中,并且您有一个名为index.php的文件

use src\Logger;

require('DulceAuth.php');

$dulceAuth = new DulceAuth($dulce);

您也可以将其包含在子目录中。例如,如果库在'mysub/'子目录中,并且您在根目录中有一个名为index.php的文件,则应该这样做

use src\Logger;

require('mysub/DulceAuth.php');

$dulceAuth = new DulceAuth($dulce);

从这里开始,您将能够使用库提供的每个可用方法 ;)

配置

数据库

请使用以下数据库表结构。这些都是最低要求的表和字段。如果您已经有了 users 表,您需要添加以下字段:name, email, password, created_at, updated_at, verified, 和 visibility。例如,如果您的表名为 "usuarios" 并且您已经有了名为 "username" 的字段用于用户的名称,您需要将该字段重命名为 name 并将表名重命名为 users。您应该对任何其他不同的字段做同样的处理。

配置文件

config 文件对每个配置选项的功能以及哪些可以修改和哪些不能修改都有详细的说明。目前它基本但功能齐全。在 src/config/config.php 中创建一个文件,并包含以下所有内容,目前只编辑 driver, host, database, usernamepassword。此外,调整常量 WEB_PAGE。其他部分我们将保持原样。

// Typical database connection
$driver = 'mysql';
$host = 'localhost';
$database = ''; // Name of your database
$username = ''; // Database user
$password = ''; // Database password
$charset = 'utf8mb4';
$collation = 'utf8mb4_unicode_ci';
$prefix = '';

// Define the project base route
define('BASE_PATH', dirname(__DIR__, 2)); // Return to the root of the project from src/config/

// Define other important routes
define('CONFIG_PATH', BASE_PATH . '/src/config/');
define('BOOTSTRAP_PATH', BASE_PATH . '/Bootstrap.php');
define('CLASE_PATH', BASE_PATH . '/DulceAuth.php');

// Define common constants here
define('WEB_PAGE', 'yourwebsite.com'); // without http(s), without www and without ending in /
// some examples: define('WEB_PAGE', 'yourwebsite.com'); or define('WEB_PAGE', 'yourwebsite.com/myFolder');

// Error log
define('EXCEPTIONS_LOG', BASE_PATH . '/src/exceptions/logs/exceptions.log');

// A little configuration about emails...
define('JSON_FILE_VERIFICATION_EMAIL', BASE_PATH . '/src/config/verification_email.json'); // json template for verification email. Edit the text as you like
define('FORGOT_PASSWORD_PAGE', 'forgot.php'); // default file where the email data (token and user id) is captured
define('JSON_FILE_FORGOT_PASSWORD_EMAIL', BASE_PATH . '/src/config/forgot_password_email.json'); // json template for forgotten password email. Edit the text as you like
define('VERIFICATION_PAGE', 'verification.php'); // default file where the verification email data is captured
define('EMAIL_FROM', 'admin@yourwebsite.com');

// Roles. At the moment do not modify anything!!
define('DEFAULT_ROLE', 'User'); // default role
define('DEFAULT_VISIBILITY', 'public'); // default profile visibility

// Accounts
define('VERIFIED', '0'); // 0 = unverified account, requires email validation. 1 = verified
define('MAX_PASSWORD_CHANGES', 3); // password changes allowed per year

// Sessions
define('SESSION_EXPIRATION', 60 * 60); // session lifetime.
//For 1 day: define('SESSION_EXPIRATION', 60 * 60 * 24);
//For 2 days: define('SESSION_EXPIRATION', 60 * 60 * 24 * 2);
//For 7 days: define('SESSION_EXPIRATION', 60 * 60 * 24 * 7);
//For 1 hour: define('SESSION_EXPIRATION', 60 * 60);

异常处理

异常根据类型组织,这意味着它们是关于角色、令牌还是用户的。例如,在注册用户时,可能会出现已经存在具有该电子邮件地址的用户的情况。在这种情况下,将抛出的异常是 DuplicateEmailException,位于 src\exceptions\users

以下是所有可能的异常列表

用户

令牌

角色和权限

在解释代码时,我们将看到何时以及如何使用异常。在某些情况下,捕获异常是必要的,而在其他情况下则是可选的。在注册用户时,您应该捕获可能发生的任何异常;然而,当用户“登录”时,则不必要。在后一种情况下,可能更建议显示更个性化的消息,因为登录方法将返回“true”或“false”。

请注意,每个方法都可以抛出它自己的异常,但每个方法也有一个通用的异常。例如,在创建新角色时,您可以选择捕获可能发生的各种异常,例如角色已被使用(UsedRoleNameException)或为空(EmptyRoleNameException)等。在这种情况下,您可以分别捕获这些特定的异常,或者“忽略”它们并捕获通用的异常,在这种情况下将是RolesException

对于令牌和用户,每种情况也有一个通用的异常。

Logger类

Logger类是一个简单的类,允许您将错误记录到文件中,以跟踪所有发生的错误或异常。每次我们包含一个try-catch块,以及异常,我们都应该包括Logger类

try {
    // ... code
} catch (Exception $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
}

这将导致异常被记录在文件中。默认情况下,此文件位于src/exceptions/logs/exceptions.log,可以通过配置文件中的EXCEPTIONS_LOG常量进行修改。

注册用户

register(string $name, string $email, string $password, array $options = [])

默认情况下,作为最小要求,此方法需要三个字段:用户、电子邮件和密码

例如

try {
    $dulceAuth->register('Test', 'test@demo.com', '1234');
} catch (Exception $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

然而,它也可以接受一个以关联数组形式存在的第四个参数。如果您想在注册新用户时存储额外数据,如国家、地址、电话号码等,第四个参数可能很有用。这种情况通常很常见。

您可以进行如下操作

$dulceAuth->register('Test', 'test@demo.com', '1234', ['country' => 'España']);

这假设您在 users 表中有一个名为 country 的字段。

您可能还想修改 "verified" 的值。例如,如果您希望新创建的账户默认为已验证,您应该这样做

$dulceAuth->register('Test', 'test@demo.com', '1234', ['verified' => 1]);

或者使账户可见性为私有

$dulceAuth->register('Test', 'test@demo.com', '1234', ['verified' => 1, 'visibility' => 'private', 'country' => 'España']);

注意:您作为第四个附加参数传递的每个字段都必须存在于 'users' 表中。

如您所见,如果您想传递或注册多个附加数据点,第四个参数非常有用。最后,关于“注册”,我们有选项一次性注册多个用户。

为此,您可以使用如下循环

$count = 3;

for ($i = 1; $i <= $count; $i++) {
    try {

        $dulceAuth = new DulceAuth($dulce);

        $register = $dulceAuth->register("Test$i", "test$i@demo.com", "1234", ["verified" => 1]);
        echo "User test$i registered successfully.\n";
        var_dump($register);
    } catch (Exception $ex) {
        echo "Error registering the user {$i}: {$ex->getMessage()}\n";
        Logger::error($ex->getMessage(), $ex->getTraceAsString());
    }
}

账户验证

我们已经看到,我们有选项创建已验证或未验证的账户。使用在 config.php 文件中找到的 VERIFIED 常量,我们可以在注册时设置是否所有账户都应创建为已验证。

define('VERIFIED', '0'); // 0 = unverified account, requires email validation. 1 = verified

但我们可以像上面提到的那样在注册账户时这样做,通过将第四个参数传递给 register 方法。

$dulceAuth->register('Test', 'test@demo.com', '1234', ['verified' => 1]);

提醒

1 表示 已验证账户

0 表示账户待验证。需要电子邮件验证。

如果您想要所有账户都需要电子邮件验证,您将需要为每个新注册生成一个随机令牌,并将其发送到新注册用户电子邮件。别担心,该方法会为我们处理。但是,register() 方法仅在账户需要验证时发送电子邮件;否则,不会发送任何内容。我的意思是,在注册后立即发送另一种类型的电子邮件给用户表示感谢或告知他们详细信息等可能很有用。但最好留给未来的版本。

注意

如果您通过 register() 方法传递 'verified' 选项,则 config 中的 'VERIFIED' 常量将不予考虑。

账户验证。第二部分。

如何验证账户?很简单。一旦用户注册,register 方法就会向用户发送一封包含先前生成的令牌的电子邮件。我们需要验证它,如果一切正常,则验证账户。

用户将收到的电子邮件将包含一个类似这样的链接:yourwebsite.com/verification.php?token=TOKENGENERADO&userId=IDUSUARIO

因此,在您的应用程序的这部分,换句话说,在您想要捕获数据的地方(即验证.php 页面),您可以使用 GET 如此操作

$token = $_GET['token'];
$userId = $_GET['userId'];

并使用两种方法进行验证

validateTokenAccount: validateTokenAccount(string $token, int $userId)

verified: verified(int $userId)

例如

if ($dulceAuth->validateTokenAccount($token, $userId)) {
    // If the token is validated successfully, we verify the user's account by setting
    // the "verified" field value to 1
    $dulceAuth->verified($userId);
}

一个更详细的例子

try {
    $token = $_GET['token'];
    $userId = $_GET['userId'];

    if ((!empty($token) && isset($token)) && (!empty($userId) && isset($userId))) {
        if ($dulceAuth->validateTokenAccount($token, $userId)) {
            echo 'Account verified';
            $dulceAuth->verified($userId);
        }
    } else {
        echo 'The token or userID is empty';
    }
} catch (RelationShipTokenException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenExpiredException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

或者使用通用异常

try {
    $token = $_GET['token'];
    $userId = $_GET['userId'];

    if ((!empty($token) && isset($token)) && (!empty($userId) && isset($userId))) {
        if ($dulceAuth->validateTokenAccount($token, $userId)) {
            echo 'Account verified';
            $dulceAuth->verified($userId);
        }
    } else {
        echo 'The token or userID is empty';
    }
} catch (TokenException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

注意:发送给用户(即发送给他们的文本)的电子邮件模板以及接收令牌和用户 ID 的页面都可以通过 config 文件中的 JSON_FILE_VERIFICATION_EMAILVERIFICATION_PAGE 常量进行修改。

一个特殊情况。或者不是!

可能会发生这种情况,即我们要求所有用户在注册时验证他们的账户,但如果有账户长时间待验证怎么办?在注册时,会生成一个令牌,但在 X 时间后,它可能会过期。在这种情况下,您应该遵循以下步骤

想象一下,用户已经注册,发送了一封验证账户的邮件,但由于某种原因,用户几天后才点击链接。正如预期的那样,令牌已经过期,发送到他们邮箱的链接将不再工作。换句话说,需要生成一个新的链接或,更准确地说,一个新的令牌。为此,可以使用以下方法:generateVerificationToken(string $email, bool $send = true)

如所见,第二个参数 $send 是可选的,我们可以决定是否传递它。如果我们不传递第二个参数执行方法,该方法本身将向用户发送一封邮件,以便他们可以验证/验证他们的账户。

try {
    $verification = $dulceAuth->generateVerificationToken('test@demo.com');
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenSaveException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RuntimeException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (InvalidArgumentException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

如果我们执行上面的代码,我们不需要做任何事情。用户将收到一封包含如下链接的邮件:

yourwebsite.com/verification.php?token=GENERATEDTOKEN&userId=USERID 跟随的步骤将与之前在“账户验证。第二部分”中描述的步骤完全相同,使用 verification.php 文件和捕获必要的数据,即令牌和用户 ID。

现在,如果我们用 $send 设置为 false 调用 generateVerificationToken 方法,它将返回一个包含令牌和用户 ID 的数组。

$verification = $dulceAuth->generateVerificationToken('test@demo.com', false); // return ['userId' => $userId, 'token' => $token];

通过这种方式,我们可以自己生成电子邮件,使用 dulceMail了解这个类

例如

try {
    $verification = $dulceAuth->generateVerificationToken('test@demo.com', false);

    if ($verification) {

        $mail = $dulceAuth->dulceMail();
        $mail->from('admin@yourwebsite.com')
            ->to('test@demo.com')
            ->subject('Validate your account')
            ->message("Click on the following link:
            yourwebsite.com/verification.php?token=" . $verification['token'] . "&userId=" . $verification['userId'] . "
            to validate your account and be able to login.");
        $mail->send();

        if ($mail->send()) {
            echo "We have just sent you an email to confirm your account.
        Please check your email.";
        }
    }
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenSaveException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RuntimeException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (InvalidArgumentException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

我们自己发送电子邮件后,执行整个过程:发送电子邮件,然后捕获数据(令牌和用户 ID),并像之前一样使用 validateTokenAccountverified 函数...

登录

$dulceAuth->login($email, $password);

该方法将用户连接到系统并创建他们对应的会话。

因此,要登录用户,你只需这样做:

$dulceAuth->login('test@demo.com', '1234');

如果我们想检查用户是否连接或是否存在活动连接

$dulceAuth->isLoggedIn();

更清晰

if ($dulceAuth->isLoggedIn()) {
        echo "You are logged in!";
    }

注销、关闭或结束会话

$dulceAuth->logout();

登录后,检索用户数据可能很有用。为此,我们使用 currentUser() 函数,后面跟着我们想要显示的字段。

例如

$dulceAuth->currentUser()->name;

如果我们有一个名为 "country" 的国家字段,我们可以这样做:

$dulceAuth->currentUser()->country;

对于当前登录用户想要显示的每个字段,可以类似地做。

用户

有几种显示数据库中所有用户列表的方法。例如,假设我们想要显示现有用户列表,但我们只对显示他们的 ID、姓名、电子邮件和国家感兴趣。为此,我们可以使用 $dulceAuth->showUsers() 方法,并用 foreach 循环遍历它

foreach ($dulceAuth->showUsers() as $user) {
        echo 'ID: ' . $user->id . '<br>';
        echo 'Name: ' . $user->name . '<br>';
        echo 'Email: ' . $user->email . '<br>';
        echo 'Country: ' . $user->country . '<br>';
    }

除了强大的 $dulceAuth->showUsers() 方法,我们可以用它来遍历我们想要显示的任何用户相关字段,还有以下三个方法

$dulceAuth->showUsersById();
$dulceAuth->showUsersByName();
$dulceAuth->showUsersByEmail();

我认为它们的名称对解释每个方法的作用已经很描述性了。我们可能认为它们没有用,但谁知道,如果我们将来需要它们,它们就在那里!

用户存在吗?

如果我们想检查用户是否存在,我们有两种方法可以做到。

第一种是通过 userIdExists 方法

userIdExists(int $userId)

$dulceAuth->userIdExists(5); // check if a user with ID 5 exists

这个方法将返回 truefalse,具体取决于我们提供的用户 ID 是否存在。

另一种方法是通过电子邮件搜索或确定用户是否存在

$dulceAuth->userEmailExists('test@demo.com');

如果电子邮件存在,它将返回 true,否则返回 false

编辑用户

要编辑用户,我们使用 *editUser* 方法,它接受两个参数:一个是需要编辑的用户 ID,另一个是包含新值的选项数组。

editUser(int $userId, array $options)

示例

try {
    $dulceAuth->editUser(1, [
        'name' => 'Test',
        'email' => 'test@demo.com',
        'country' => 'Spain'
    ]);
} catch (EditUserException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (ArrayOptionsUserException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

在上面的例子中,我们编辑了 ID 为 1 的用户的名称、电子邮件和国家字段。返回值将是 truefalse

顺便说一下,回到异常的话题,前面的代码可以简化为

try {
    $dulceAuth->editUser(1, [
        'name' => 'Test',
        'email' => 'test@demo.com',
        'country' => 'Spain'
    ]);
} catch (Exception $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

但是,我建议指定每个异常 ;-)

删除用户

要删除用户,只需执行"deleteUser"方法,并传入要删除的用户ID。

deleteUser(int $userId)

try {
    $dulceAuth->deleteUser(1);
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (Exception $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

成功后将返回truefalse

创建新用户

可能有用,在不使用"register"方法的情况下创建新用户。例如,如果您有一个管理员面板,并希望有创建用户的选项,您可以使用"createUser"方法。此方法至少需要三个参数:姓名、电子邮件和密码。还有一个可选的第四个参数,它是一个数组,允许您传递要注册的附加数据,例如电话号码、国家等。

createUser(string $name, string $email, string $password, array $options = [])

以下是一个使用可选第四个参数的示例。如果我们想创建一个已验证的账户并带有电话号码,我们将这样做

try {
    $dulceAuth->createUser('Test', 'test@demo.com', '1234', [
        'verified' => 1,
        'phone' => '6XXXXXXXX'
    ]);
} catch (DuplicateEmailException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (CreateUserException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (Exception $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

更改用户密码

要更改用户密码,您需要使用三个参数执行"changePassword"方法

changePassword(int $userId, string $currentPassword, string $newPassword)

例如,要更改ID为'1'的用户的密码

try {
    $dulceAuth->changePassword(1, '1234', '1234da#');
} catch (Exception $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

dulceAuth 允许您对密码更改设置限制,默认为每年3次更改。您可以通过config.php文件中的MAX_PASSWORD_CHANGES常量来更改此设置。我认为这可以用来防止滥用和不必要的changePassword方法的使用。

了解用户(如果有)进行密码更改的总次数也可能很有帮助。为此

try {
    echo $dulceAuth->latestChange(1)->changes_count;
} catch (LimitChangesPasswordException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

"latestChange"方法接受一个参数,即要查询的用户ID,如果在找到密码更改的情况下,将返回PasswordChange的实例。然后,您可以使用它的changes_count属性来找出更改的总数。

密码恢复

如果用户忘记了他们的密码,由于密码使用了安全加密,因此无法恢复它,因此必须创建一个新的。为此,dulceAuth将生成一个临时令牌,以便用户可以创建一个新密码。

使用forgotPassword方法

forgotPassword($email, $send = true)

为提供的参数电子邮件的用户生成一个令牌。$send参数是可选的,您可以决定是否包含它。无论您是否包含它都会影响该方法的行为。基本上,默认情况下,此方法会向用户发送电子邮件,其中包含包含令牌和用户ID的链接,稍后用于创建新密码。这听起来很熟悉,因为它的操作方式与generateVerificationToken方法类似。

使用它的常见方法是

try {
    $forgotPassword = $dulceAuth->forgotPassword('test@demo.com');
} catch (TokenSaveException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RuntimeException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (InvalidArgumentException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

如果我们调用方法并将$send设置为false,它将返回一个包含令牌和用户ID的数组。

$forgotPassword = $dulceAuth->forgotPassword('test@demo.com', false); // return ['userId' => $userId, 'token' => $token];

通过这种方式,我们可以自己使用dulceMail类生成发送给用户的电子邮件。

让我们看看一些示例。

假设名为"Test"的邮箱为"test@demo.com"的用户忘记了他们的密码。在这种情况下,我们只会通过传递用户的电子邮件调用forgotPassword方法,这样方法本身就会处理发送包含令牌和用户ID的恢复链接的电子邮件。

$forgotPassword = $dulceAuth->forgotPassword('test@demo.com');

如果我们检查电子邮件并点击链接,它将带我们到设置为"密码恢复"页面的页面。密码恢复的默认页面名为forgot.php,并在config.php文件中使用FORGOT_PASSWORD_PAGE常量设置。

在本页面上,我们使用 $_GET 获取 tokenuserId 值,并将它们传递给 validateTokenPassword 方法。

validateTokenPassword(string $token, int $userId): bool

例如

$token = $_GET['token'];
$userId = $_GET['userId'];

if ($dulceAuth->validateTokenPassword($token, $userId)) {
    // If it has been validated successfully, here you can display a form to enter the new password...
}

最后,如果验证正确,我们将使用 insertNewPassword 方法创建新密码。

insertNewPassword(string $password, int $userId): void

该方法接受新密码和用户 ID 作为参数。

一个更完整的例子可能是 forgot.php 页面。

try {
    $token = $_GET['token'];
    $userId = $_GET['userId'];

    if ((!empty($token) && isset($token)) && (!empty($userId) && isset($userId))) {
        if ($dulceAuth->validateTokenPassword($token, $userId)) {
            $dulceAuth->insertNewPassword('new password', $userId);
            echo 'Password changed successfully';
        }
    } else {
        echo 'The token or userID is empty';
    }
} catch (RelationShipTokenException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenExpiredException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (TokenException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

我们已经看到了推荐的简单方法。现在,让我们继续探讨第二种方法,该方法本质上涉及到我们自行发送/生成恢复邮件,使用 dulceMail 类。为此,调用 forgotPassword 方法,传递用户的电子邮件,并将第二个参数设置为 false。这样,它只会返回用户 ID 和生成的临时 token。下一步就是我们自己生成电子邮件并发送给用户。

try {
    $forgotPassword = $dulceAuth->forgotPassword('test@demo.com', false); // return ['userId' => $userId, 'token' => $token];
    // We check that `$forgotPassword` returns the userId and the token.
    if ($forgotPassword) {
        $mail = $dulceAuth->dulceMail();
        $mail->from('admin@yourwebsite.com')
            ->to('test@demo.com')
            ->subject('Password Regeneration')
            ->message("You received this email because you forgot your password and a token has been generated to reset it.
        Click on the following link: yourwebsite.com/forgot.php?token=" . $forgotPassword['token'] . "&userId=" . $forgotPassword['userId'] . " \n
        If it wasn't you, please contact administration urgently as your account may be at risk.");
        $mail->send();
        // If the sending is successful, we can display a message in the browser.
        if ($mail->send()) {
            echo "We have just sent you an email.
        Please check your email to create a new password that you will remember.";
        }
    }
} catch (TokenSaveException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RuntimeException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (InvalidArgumentException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

就这样!从现在开始,过程和之前一样。在点击密码恢复页面的链接后,我们获取 tokenuserId 值,并将它们传递给 validateTokenPassword 方法,然后调用 insertNewPassword 方法。

非常重要

insertNewPassword 方法必须在生成 token 后调用;否则,将抛出异常。

换句话说,直接调用该方法而不首先生成 token 将不会更改密码。

建议按照以下顺序进行:首先,为电子邮件生成 token(使用 forgotPassword),然后使用 validateTokenPassword 验证它,最后使用 insertNewPassword 插入/注册新密码。

角色和权限

创建新角色

如果我们想创建新角色,只需调用 createRole 函数,并传递一个有效的名称作为参数。例如

try {
    $dulceAuth->createRole('new_role_name');
} catch (EmptyRoleNameException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RoleSaveException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UsedRoleNameException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

或者

try {
    $dulceAuth->createRole('new_role_name');
} catch (RolesException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

编辑角色

要编辑角色,使用以下方法:editRole(int $roleId, string $name):第一个参数是角色 ID,第二个是我们想要分配的新名称。

try {
    $dulceAuth->editRole(10, 'new_name');
} catch (RoleNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (EmptyRoleNameException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UsedRoleNameException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RoleSaveException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

或者也可以

try {
    $dulceAuth->editRole(10, 'new_name');
} catch (RolesException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

但实际上,最好不要这样做;P

删除角色

要删除角色,预期会调用 deleteRole 方法,并传递要删除的角色 ID。

try {
    $eliminarRol = $dulceAuth->deleteRole(8);
    if ($eliminarRol) {
        echo 'Role successfully deleted...';
    }
} catch (Exception $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

分配角色给用户

现在,如果我们已经创建了一系列角色,为用户分配一个或多个角色可能很有用。为此,我们调用 assignRoleToUser 方法,该方法有两个参数:assignRoleToUser(int $userId, array $roles): bool

一个参数是用户 ID,另一个是我们想要分配的角色 ID。

假设我们想将三个角色分配给用户 Jhon,其 ID 为 27,并将角色分配为:'editor'、'user' 和 'moderator',分别对应 ID 4、5 和 7。

为此,我们应该做以下操作

try {
    $asignarRol = $dulceAuth->assignRoleToUser(27, [4, 5, 7]);
    if ($asignarRol) {
        echo 'Role(s) successfully assigned.';
    }
} catch (RoleNotSelectedException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RoleAssignmentException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

值得注意的是,如果您分配了一个用户已经拥有的角色,则不会发生任何事情;其余的角色将按常规分配。

现在我们已经将角色分配给了用户,我们也可以从用户中 移除 角色。为此,我们有 removeRoleFromUser 方法:removeRoleFromUser(int $userId, array $roles): bool

此方法接受与上一个方法相同的参数:第一个是用户 ID,第二个是一个包含角色 ID 的数组。它必须接收至少一个要删除的角色。

例如

try {
    $eliminarRol = $dulceAuth->removeRoleToUser(27, [2, 7]);
    if ($eliminarRol) {
        echo 'Role(s) successfully removed from the user.';
    }
} catch (RoleNotSelectedException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RoleNotAssignedException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

权限

权限与角色相关,反之亦然。用户将根据其拥有的角色拥有某些权限,因此建议创建一个权限列表。我们想要创建的角色和权限数量将取决于应用程序的类型和我们要实施的授权。

创建权限

首先,如果我们想创建新的权限,我们需要使用方法 createPermissioncreatePermission(string $name, string $description): bool

此方法接受两个参数(名称和描述)。描述是可选的,但很有用。

try {
    $newPermission = $dulceAuth->createPermission('Post news', 'Allows you to publish news on the site');
    if ($newPermission) {
        echo 'Permission created successfully!';
    }
} catch (EmptyPermissionNameException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UsedPermissionNameException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (PermissionSaveException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

编辑权限

如果我们想编辑已创建权限的名称,我们需要调用方法 editPermissioneditPermission(int $permissionId, string $newName): bool

此方法接受两个参数:一个整数和一个字符串。整数代表权限的ID,字符串代表我们想要分配给它的新名称。

例如

try {
    $editPermission = $dulceAuth->editPermission(13, 'Post article');
    if ($editPermission) {
        echo 'Permission edited successfully!';
    }
} catch (InvalidArgumentException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (EmptyPermissionNameException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (UsedPermissionNameException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (PermissionSaveException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (PermissionNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

记住,你可以“忽略”特定的异常并捕获通用的异常。

try {
    $editPermission = $dulceAuth->editPermission(13, 'Post article');
    if ($editPermission) {
        echo 'Permission edited successfully!';
    }
} catch (Exception $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

但再次强调,我个人不建议这么做 ;-)

删除权限

要删除权限,只需运行以下方法: deletePermission(int $permissionId): bool

try {
    $removePermission = $dulceAuth->deletePermission(13);
    if ($removePermission) {
        echo 'Permission removed successfully!';
    }
} catch (PermissionNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

将权限分配给角色

如我之前所述,权限本身没有任何作用;如果没有关联的角色,它就没有用处。因此,需要将其链接到一个角色。为此,你应该使用两个参数(角色的ID和权限的ID)调用方法 assignPermissionToRoleassignPermissionToRole(int $roleId, int $permissionId)

try {
    $assignPermissionToRole = $dulceAuth->assignPermissionToRole(4, 14);
    if ($assignPermissionToRole) {
        echo 'Permission successfully assigned to role';
    }
} catch (MissingRoleOrPermissionException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RoleNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (PermissionNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RolePermissionAlreadyExistsException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

从角色中删除权限

如果我们想从角色中删除权限,我们需要执行方法 removePermissionFromRoleremovePermissionFromRole(int $roleId, int $permissionId)

其中,同样地,第一个参数是角色ID,第二个参数是权限ID。

例如

try {
    $removeRolePermission = $dulceAuth->removePermissionFromRole(4, 14);
    if ($removeRolePermission) {
        echo 'Permission successfully REMOVED from the role';
    }
} catch (MissingRoleOrPermissionException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (RoleNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (PermissionNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

角色和权限 V.2

之前,我们看到了如何创建角色和权限,以及如何将它们分配给用户。现在,有一些方法允许我们查看所有可用的角色和权限,以及列出用户拥有的所有角色。

重要的是要提到,列出用户的全部权限是不可能的,因为权限是 绑定 到角色的,而不是绑定到用户。换句话说,用户是否拥有权限取决于他们拥有的角色。

明确了这一点后,让我们看看之前提到的那些方法。

要查看所有可用的角色,我们调用方法 showRoles() 并使用 foreach 遍历它

$roles = $dulceAuth->showRoles();

foreach ($roles as $role) {
    echo "ROLE ID: $role->id | NAME: $role->name <br>";
}

还有两个额外的 方法: showRolesById()showRolesByName(),允许你“更快”地列出角色的ID或名称。然而,主要的方法(showRoles())通常就足够了。

相同的方法也适用于权限。我们有 showPermissions() 方法来列出所有可用的权限

$permissions = $duleAuth->showPermissions();

foreach ($permissions as $permission) {
    echo "PERMISSION ID: $permission->id | PERMISSION NAME: $permission->name <br>";
}

就像之前一样,我们有 showPermissionsById()showPermissionsByName() 方法,分别通过ID或名称显示权限。

所有这些方法实际上在像管理面板这样的场景中很有用,在那里有必要看到所有创建的角色和权限列表。

如果你想知道特定用户拥有哪些角色,还有一个有趣的方法。

userRoles($userId)

此方法接受一个参数,即你想检查的用户ID。

例如,如果你想检查ID为“2”的用户拥有哪些角色,你会这样做

try {
    $roles = $dulceAuth->userRoles(2);
    echo 'The user with ID 2 has the following roles: <br>';
    foreach ($roles as $role) {
        echo "ROLE: $role->name <br>";
    }
} catch (UserNotFoundException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}

同样,就像之前的例子一样,还有两种方法可以显示通过ID或名称的角色

userRolesById($userId)userRolesByName($userId)

我们可能永远都不会使用它们,但至少现在有了!

授权

我们已经创建了用户、角色和权限...现在,我们对在应用程序中实现授权很感兴趣。例如,我们可能只想让管理员访问管理区域,或者在执行特定操作之前验证用户是否拥有必要的角色。

为了实现这一点,我们有两种方法来验证用户是否拥有所需的角色或权限。第一个方法是

hasRole(string $roleName, ?int $userId = null): bool

此方法用于检查当前用户(即已登录的用户)是否具有特定的角色。您只需传递角色名称即可

if ($dulceAuth->hasRole('Admin')) {
    echo 'You are an administrator.';
    // Here you can add an administration area or anything else...
} else {
    echo 'You do not have the required role to view this page.';
}
// or something more complete and detailed:
$dulceAuth->login('test@demo.com', '12345');

if ($dulceAuth->isLoggedIn()) {
    echo "You are connected!";
    if ($dulceAuth->hasRole('Admin')) {
        echo 'You are an administrator.';
        // Here you can add an administration area or anything else...
    } else {
        echo 'You do not have the required role to view this page';
    }
}

正如我们所见,hasRole 方法还可以接受第二个可选参数,即用户的身份。如果我们想检查特定用户是否具有特定角色,这非常有用。

例如

if ($dulceAuth->hasRole('SuperAdmin', 1)) {
    echo 'The user has the role!';
} else {
    echo 'The user does not have the role! :-(';
}

在先前的例子中,我们检查的不是当前用户是否是“SuperAdmin”,而是检查身份为1的用户。

对于权限,也存在一个类似的功能,但到目前为止,它只检查当前用户的权限,即登录到应用程序的用户。

例如,假设我们有一个创建用户的权限,我们可以检查当前用户是否有这个权限。

为此,我们执行 hasPermission 方法,将权限名称作为参数传递。

if ($dulceAuth->hasPermission('Create user')) {
    echo 'You have the necessary permission to create users.';
} else {
    echo 'You do not have permission to perform this action.';
}

会话

关于会话,没有太多可说的。它只是一个用于创建和检索会话变量的简单类。其用法很简单,有两个主要方法

set(string $key, $value) 用于创建会话。

get(string $key) 用于获取会话。

$dulceAuth->session()->set('color', 'red');
echo $dulceAuth->session()->get('color');

我们还有一些其他的方法,包括

has($key):检查会话中是否存在键。

$dulceAuth->session()->has('color'); // returns true or false

if ($dulceAuth->session()->has('color')) {
    echo 'It exists!';
} else {
    echo 'It does not exist!';
}

remove($key):从会话中删除键值对。

$dulceAuth->session()->remove('name');

destroy():完全销毁会话。

$dulceAuth->session()->destroy(); // destroys the entire session.

重要的是要提到,当用户登录时,会自动创建两个会话。一个是用户的身份,称为“userId”,另一个是活动会话的持续时间,命名为“expire_time”。

例如,要检索这些会话,比如登录用户的ID,我们需要使用 Session 类的 get() 方法,如下所示

echo $dulceAuth->session()->get('userId');

会话时长

如前所述,当用户登录时,会创建两个会话:一个包含用户ID,另一个包含会话的持续时间/过期时间。

目前,默认会话持续时间设置为1小时,但可以通过在 config.php 文件中找到的常量 SESSION_EXPIRATION 来更改。

要确定会话的确切持续时间,您可以检查“expire_time”的值,如下所示

$dulceAuth->session()->get('expire_time');

然而,如果您这样做,您将得到以 timestamp 格式的日期,这有点难以阅读。因此,有一个更好的方法叫做 expirationTime(),它允许您以更可读的格式检查这些数据。

echo $dulceAuth->session()->expirationTime(); // displays time in format: Y-m-d H:i:s

在访问应用程序的某个部分之前,检查不仅用户是否已登录(请记住,isLoggedIn() 方法是为此而设计的),而且还存在一个 活动且有效的会话。 要这样做,您可以使用 Session 类的 isValid() 方法,如下所示

if ($dulceAuth->session()->isValid()) {
        echo 'The session is active';
        // We can do anything here...
    } else {
        echo 'The session has expired';
    }

dulceMail

这是一个非常简单的类,本质上使用PHP的本地 mail() 函数。它主要用于以最简单的方式发送和接收电子邮件。请记住这一点。如果您需要更安全的方法,请考虑探索像“PHPMailer”这样的替代方案,并将其与 dulceAuth 集成。

要使用此类,其用法很简单

$mail = $dulceAuth->dulceMail();
// We prepare the email: sender, recipient, subject and message
$mail->from('admin@yourwebsite.com')->to('test@demo.com')->subject('Subject/topic')->message('Any message...');
// and we send it
$mail->send();

但就像往常一样,一个包含异常的更完整的示例如下

try {
    $mail = $dulceAuth->dulceMail();

    $mail->from('admin@yourwebsite.com')->to('test@demo.com')->subject('Subject/topic')->message('Any message...');

    $send = $mail->send();
    // If the sending is successful, we can display a message in the browser.
    if ($send) {
        echo 'We just sent you an email.';
    }
} catch (RuntimeException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
} catch (InvalidArgumentException $ex) {
    Logger::error($ex->getMessage(), $ex->getTraceAsString());
    echo $ex->getMessage();
}