raid/core-auth

Raid Core Auth 包

dev-main 2024-01-28 21:39 UTC

This package is auto-updated.

Last update: 2024-09-28 23:08:13 UTC


README

该包负责处理系统中所有认证模型和通道。

安装

composer require raid/core-auth

配置

php artisan core:publish-auth

使用

class AuthController extends Controller
{
    /**
     * Invoke the controller method.
     */
    public function __invoke(Request $request, SystemAuthChannel $authChannel): JsonResponse
    {
        $credentials = $request->only([
            'email', 'phone', 'username', 'password',
        ]);

        $authChannel = $authChannel->authenticate(new User(), $credentials);

        // or using static call
        $authChannel = SystemAuthChannel::auth(User::class, $credentials);

        // or using facade
        $authChannel = Authentication::authenticate(new User(), $credentials);

        return response()->json([
            'channel' => $authChannel->channel(),
            'token' => $authChannel->stringToken(),
            'errors' => $authChannel->errors()->toArray(),
            'resource' => $authChannel->account(),
        ]);
    }
}

如何使用

认证过程分为两部分。

第一部分是可认证类,第二部分是认证通道。

Authenticatable 类是将会被认证的类,必须实现 AuthenticatableInterface 接口。

AuthChannel 类是处理认证过程的类,必须实现 AuthChannelInterface 接口。

AuthChannel 使用 Authenticatable 类通过提供的凭证查询账户。

Authenticatable 类必须定义 getAccount 方法来查询账户,并返回 AccountInterface 接口的一个实例。

Authenticatable 类可以是也可以不是 AccountInterface 类,但如果找到了账户,它必须查询账户并返回一个 AccountInterface 接口的实例。

让我们以我们的 AccountInterface 类的例子 User 模型开始,我们可以使用此命令创建一个账户模型。

php artisan core:make-auth-model User
<?php

namespace App\Models;

use Raid\Core\Auth\Models\Authentication\Contracts\AccountInterface;
use Raid\Core\Auth\Models\Authentication\Account;

class User extends Account implements AccountInterface
{
    /**
     * {@inheritdoc}
     */
    protected $fillable = [];
}

Model 类必须实现 AccountInterface 接口。

Model 类必须扩展 Account 类。

现在 User 模型类已经准备好作为账户模型使用了。

让我们配置我们的 Model 类以作为 Authenticatable 类使用。

<?php

namespace App\Models;

use Raid\Core\Auth\Models\Authentication\Contracts\AuthenticatableInterface;
use Raid\Core\Auth\Models\Authentication\Contracts\AccountInterface;
use Raid\Core\Auth\Models\Authentication\Account;
use Raid\Core\Auth\Traits\Model\Authenticatable;

class User extends Account implements AccountInterface, AuthenticatableInterface
{
    use Authenticatable;

    /**
     * {@inheritdoc}
     */
    protected $fillable = [];
}

认证通道和工作者

很好,现在我们来看看我们的认证通道和工作者在 config/authentication.php 文件中的设置。

'channel_workers' => [
    // here we define our auth channels.
    SystemAuthChannel::CHANNEL => [
        // here we define our auth workers.
        EmailAuthWorker::class,
        PhoneAuthWorker::class,
        EmailOrPhoneAuthWorker::class,
    ],
],

channel_workers 数组负责定义认证通道及其工作者,并且我们可以添加自定义的通道和工作者。

AuthChannel 负责处理认证过程。

每个认证通道都有自己的工作者,每个认证工作者都有自己的认证名称/工作者。

当我们调用 AuthChannel 的 authenticate 方法时,它将调用与给定凭证匹配的认证工作者。

我们可以添加一个自定义认证工作者,使用它与 SystemAuthChannel 通道或任何其他认证通道一起使用。

认证工作者

你可以使用此命令创建一个新的认证工作者。

php artisan core:make-auth-worker UsernameAuthWorker
<?php

namespace App\Http\Authentication\Workers;

use Raid\Core\Auth\Authentication\Contracts\AuthWorkerInterface;
use Raid\Core\Auth\Authentication\AuthWorker;

class UsernameAuthWorker extends AuthWorker implements AuthWorkerInterface
{
    /**
     * {@inheritdoc}
     */
    public const WORKER = '';
}

AuthWorker 类必须实现 AuthWorkerInterface 接口。

AuthWorker 类必须扩展 AuthWorker 类。

WORKER 常量负责定义认证工作者的名称。

WORKER 常量用于将认证工作者与给定的凭证匹配。

WORKER 常量用于定义可认证查询列,以覆盖使用相同的密钥用于凭证和查询,定义 QUERY_COLUMN 常量。

<?php

namespace App\Http\Authentication\Workers;

use Raid\Core\Auth\Authentication\Contracts\AuthWorkerInterface;
use Raid\Core\Auth\Authentication\AuthWorker;

class UsernameAuthWorker extends AuthWorker implements AuthWorkerInterface
{
    /**
     * {@inheritdoc}
     */
    public const WORKER = 'username';
    
    /**
     * {@inheritdoc}
     */
    public const QUERY_COLUMN = 'user_name';
}

QUERY_COLUMN 常量用于定义可认证查询列。

我们需要在 config/authentication.php 文件中定义新的认证工作者,要跳过使用配置文件,我们可以在 AuthChannel 类中使用 workers 方法来定义其工作者。

'manager_workers' => [
    // here we define our auth managers.
    SystemAuthChannel::CHANNEL => [
        // here we define our auth workers.
        EmailAuthWorker::class,
        PhoneAuthWorker::class,
        EmailOrPhoneAuthWorker::class,
        UsernameAuthWorker::class,
    ],
],

现在让我们尝试我们的新认证工作者。

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Raid\Core\Auth\Authentication\Channels\SystemAuthChannel;

class AuthController extends Controller
{
    /**
     * Invoke the controller method.
     */
    public function __invoke(Request $request, SystemAuthChannel $authChannel): JsonResponse
    {
        $credentials = $request->only([
            'username', 'password',
        ]);

        $authChannel = $authChannel->authenticate(new User(), $credentials);

        return response()->json([
            'channel' => $authChannel->channel(),
            'token' => $authChannel->stringToken(),
            'errors' => $authChannel->errors()->toArray(),
            'resource' => $authChannel->account(),
        ]);
    }
}

SystemAuthChannel 的 authenticate 方法接受两个参数。

第一个参数是可认证类的实例。

第二个参数是凭证数组。

SystemAuthChannel 的 authenticate 方法返回 AuthChannel 类的实例。

AuthChannel 类的实例使用凭证数组与认证工作者匹配。

使用匹配的worker查询可认证类,以找到匹配的账户的AuthChannel类实例。

找到账户后,AuthChannel类应用其自己的认证规则。

Authenticatable类实例必须与查询构建器一起工作以找到账户。

在内部,AuthWorker类在Authenticatable类实例中调用getAccount方法。

返回的账户必须是AccountInterface接口的实例。

找到账户并应用AuthChannel规则后,可以使用isAuthenticated方法对账户本身应用认证规则。

<?php

namespace App\Models;

use Raid\Core\Auth\Exceptions\Authentication\AuthenticationException;
use Raid\Core\Auth\Models\Authentication\Contracts\AuthenticatableInterface;
use Raid\Core\Auth\Models\Authentication\Contracts\AccountInterface;
use Raid\Core\Auth\Models\Authentication\Account;
use Raid\Core\Auth\Traits\Model\Authenticatable;

class User extends Account implements AccountInterface, AuthenticatableInterface
{
    use Authenticatable;
    
    /**
     * Check if an account is active to authenticated.
     * Throw Authentication exception if failed to authenticate.
     */
    public function isAuthenticated(): void
    {
        if ($this->isBanned()) {
            throw new AuthenticationException(__('User is banned.'));
        }
    }
    
        /**
     * Determine if user is banned.
     */
    public function isBanned(): bool
    {
        return $this->attribute('is_banned', false);
    }
}

isAuthenticated方法负责检查账户是否已认证。

如果账户未认证,isAuthenticated方法应抛出异常。

系统不会抛出AuthenticationException,但可以使用AuthChannel实例中的errors方法调用错误。

errors方法将返回一个Raid\Core\Model\Errors\Errors实例。

记住,除了AuthenticationException以外的任何其他异常都将在系统中抛出。

错误

您可以像使用Illuminate\Support\MessageBag实例一样使用errors方法,并且可以通过不同的方法获取错误。

$authChannel = $authChannel->authenticate(new User(), $credentials);

$errorsAsArray = $authChannel->errors()->toArray();
$errorsAsJson = $authChannel->errors()->toJson();

$allErrors = $authChannel->errors()->all();

$errorsByKey = $authChannel->errors()->get('error');

$firstError = $authChannel->errors()->first();
$firstErrorByKey = $authChannel->errors()->first('error');

$lastError = $authChannel->errors()->last();
$lastErrorByKey = $authChannel->errors()->last('error');

errors方法返回一个Raid\Core\Model\Errors\Errors类实例。

toArray方法返回错误数组。

toJson方法返回错误json字符串。

all方法返回错误数组。

get方法返回给定键的错误数组。

first方法返回第一个错误,或给定键的第一个错误。

last方法返回最后一个错误,或给定键的最后一个错误。

您可以在AuthChannel类中再次使用errors方法。

认证渠道

您可以使用此命令创建自己的认证渠道。

php artisan core:make-auth-channel OtpAuthChannel
<?php

namespace App\Http\Authentication\Channels;

use Raid\Core\Auth\Authentication\Contracts\AuthChannelInterface;
use Raid\Core\Auth\Authentication\AuthChannel;

class OtpAuthChannel extends AuthChannel implements AuthChannelInterface
{
    /**
     * {@inheritdoc}
     */
    public const CHANNEL = '';
}

AuthChannel类必须实现AuthChannelInterface接口。

AuthChannel类必须扩展AuthChannel类。

CHANNEL常量负责定义认证渠道名称。

认证渠道是处理认证过程的主要类,它定义了自己的认证workersrulessteps

<?php

namespace App\Http\Authentication\Channels;

use Raid\Core\Auth\Authentication\Contracts\AuthChannelInterface;
use Raid\Core\Auth\Authentication\AuthChannel;

class OtpAuthChannel extends AuthChannel implements AuthChannelInterface
{
    /**
     * {@inheritdoc}
     */
    public const CHANNEL = 'otp';

    /**
     * Get authentication workers.
     */
    public function workers(): array
    {
        return [];
    }
    
    /**
     * Get authentication rules.
     */
    public function rules(): array
    {
        return [];
    }
    
    /**
     * Get authentication steps.
     */
    public function steps(): array
    {
        return [];
    }
}

workers方法负责定义AuthChannel认证worker。

workers方法应返回认证worker数组。

rules方法负责定义AuthChannel认证规则。

rules方法应返回认证规则数组。

steps方法负责定义AuthChannel认证步骤。

steps方法应返回认证步骤数组。

规则在步骤之前运行。

在运行AuthChannel认证步骤后,认证过程将停止,并且AuthChannel将返回AuthChannel实例。

我们可以通过返回空数组来跳过使用认证规则和步骤。

认证规则

您可以使用此命令创建新的认证规则。

php artisan core:make-auth-rule VerifiedPhoneAuthRule
<?php

namespace App\Http\Authentication\Rules;

use Raid\Core\Auth\Authentication\Contracts\AuthChannelInterface;
use Raid\Core\Auth\Authentication\Contracts\AuthRuleInterface;

class VerifiedPhoneAuthRule implements AuthRuleInterface
{
    /**
     * Run an authentication ruler.
     */
    public function rule(AuthChannelInterface $authChannel): bool
    {
    }
}

AuthRule类必须实现AuthRuleInterface接口。

rule方法负责执行认证规则。

rule方法应返回布尔值。

rule方法应在认证规则通过时返回true

rule方法应在认证规则失败时将错误添加到AuthChannel并返回false

<?php

namespace App\Http\Authentication\Rules;

use Raid\Core\Auth\Authentication\Contracts\AuthChannelInterface;
use Raid\Core\Auth\Authentication\Contracts\AuthRuleInterface;

class VerifiedPhoneAuthRule implements AuthRuleInterface
{
    /**
     * Run an authentication rule.
     */
    public function rule(AuthChannelInterface $authChannel): bool
    {
        if ($authChannel->account()->verifiedPhone()) {
            return true;
        }

        $authChannel->errors()->add('error', __('Phone number is not verified.'));

        return false;
    }
}

我们需要在AuthChannel类中定义新的认证规则。

<?php

namespace App\Http\Authentication\Channels;

use App\Http\Authentication\Rules\VerifiedPhoneAuthRule;
use Raid\Core\Auth\Authentication\Contracts\AuthChannelInterface;
use Raid\Core\Auth\Authentication\AuthChannel;

class OtpAuthChannel extends AuthChannel implements AuthChannelInterface
{
    /**
     * {@inheritdoc}
     */
    public const CHANNEL = 'otp';

    /**
     * Get authentication rules.
     */
    public function rules(): array
    {
        return [
            VerifiedPhoneAuthRule::class,
        ];
    }
}

AuthChannel类实例将在认证规则失败时停止认证过程。

认证步骤

您可以使用此命令创建新的认证步骤。

php artisan core:make-auth-step OtpAuthStep
<?php

namespace App\Http\Authentication\Steps;

use Raid\Core\Auth\Authentication\Contracts\AuthChannelInterface;
use Raid\Core\Auth\Authentication\Contracts\AuthStepInterface;

class OtpAuthStep implements AuthStepInterface
{
    /**
     * Run an authentication step.
     */
    public function step(AuthChannelInterface $authChannel): void
    {
    }
}

AuthStep 类必须实现 AuthStepInterface 接口。

step 方法负责执行认证步骤。

如果认证步骤失败,step 方法应将 errors 添加到 AuthChannel

<?php

namespace App\Http\Authentication\Steps;

use App\Services\OtpService;
use Exception;
use Raid\Core\Auth\Authentication\Contracts\AuthChannelInterface;
use Raid\Core\Auth\Authentication\Contracts\AuthStepInterface;

class OtpAuthStep implements AuthStepInterface
{
    /**
     * Otp service.
     */
    protected OtpService $otpService;

    /**
     * Otp service.
     */
    public function __construct(OtpService $otpService)
    {
        $this->otpService = $otpService;
    }

    /**
     * Run an authentication step.
     */
    public function step(AuthChannelInterface $authChannel): void
    {
        try {
        
            $this->otpService->send($authChannel->account());
            
        } catch (Exception $exception) {
            $authChannel->errors()->add('error', $exception->getMessage());
        }
    }
}

我们需要在 AuthChannel 类中定义新的认证步骤。

<?php

namespace App\Http\Authentication\Channels;

use App\Http\Authentication\Steps\OtpAuthStep;
use Raid\Core\Auth\Authentication\Contracts\AuthChannelInterface;
use Raid\Core\Auth\Authentication\AuthChannel;

class OtpAuthChannel extends AuthChannel implements LoginProviderInterface
{
    /**
     * {@inheritdoc}
     */
    public const CHANNEL = 'otp';

    /**
     * Get authentication steps.
     */
    public function steps(): array
    {
        return [
            OtpAuthStep::class,
        ];
    }
}

我们可以运行我们的认证步骤。

在运行所有认证步骤后,AuthChannel 类的实例将停止认证过程。

认证器

您可以使用此命令创建自己的认证器。

php artisan core:make-auth-authenticator UserAuthenticator
<?php

namespace App\Http\Authentication\Authenticators;

use Raid\Core\Auth\Authentication\Contracts\AuthenticatorInterface;
use Raid\Core\Auth\Authentication\Authenticator;

class UserAuthenticator extends Authenticator implements AuthenticatorInterface
{
    /**
     * {@inheritdoc}
     */
    public const AUTHENTICATOR = '';

    /**
     * {@inheritdoc}
     */
    public const AUTHENTICATABLE = '';

    /**
     * {@inheritdoc}
     */
    public const CHANNELS = [];

Authenticator 类必须实现 AuthenticatorInterface 接口。

Authenticator 类必须扩展 Authenticator 类。

AUTHENTICATOR 常量负责定义认证器名称。

AUTHENTICATABLE 常量负责定义可认证的类名称。

CHANNELS 常量负责定义认证器通道。

CHANNELS 常量应返回一个认证器通道数组。

<?php

namespace App\Http\Authentication\Authenticators;

use App\Http\Authentication\Channels\OtpAuthChannel;
use App\Models\User;
use Raid\Core\Auth\Authentication\Channels\SystemAuthChannel;
use Raid\Core\Auth\Authentication\Contracts\AuthenticatorInterface;
use Raid\Core\Auth\Authentication\Authenticator;

class UserAuthenticator extends Authenticator implements AuthenticatorInterface
{
    /**
     * {@inheritdoc}
     */
    public const AUTHENTICATOR = 'user';

    /**
     * {@inheritdoc}
     */
    public const AUTHENTICATABLE = User::class;

    /**
     * {@inheritdoc}
     */
    public const CHANNELS = [
        OtpAuthChannel::class,
        SystemAuthChannel::class,
    ];
}

Authenticator 类的实例负责处理不同通道的认证。

我们可以用两种方式定义我们的认证器

首先在 config/authentication.php 文件中。

'authenticators' => [
    User::class => UserAuthenticator::class,
],

或者在 Authenticatable 类中定义 getAuthenticator 方法。

<?php

namespace App\Models;

use Raid\Core\Auth\Models\Authentication\Contracts\AuthenticatableInterface;
use Raid\Core\Auth\Models\Authentication\Contracts\AccountInterface;
use Raid\Core\Auth\Models\Authentication\Account;
use Raid\Core\Auth\Traits\Model\Authenticatable;
use App\Http\Authentication\Authenticators\UserAuthenticator;

class User extends Account implements AccountInterface, AuthenticatableInterface
{
    use Authenticatable;

    /**
     * Get authenticator class name.
     */
    public function getAuthenticator(): string
    {
        return UserAuthenticator::class;
    }
}

现在让我们尝试我们的新认证器。

namespace App\Http\Controllers;

use App\Http\Authentication\Authenticators\UserAuthenticator;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    /**
     * Invoke the controller method.
     */
    public function __invoke(Request $request): JsonResponse
    {
        $credentials = $request->only([
            'username', 'password',
        ]);

        $authManager = UserAuthenticator::attempt($credentials, 'otp');
        
        // or use authenticatable static call
        $authManager = User::attempt($credentials, 'otp');

        return response()->json([
            'channel' => $authChannel->channel(),
            'token' => $authChannel->stringToken(),
            'errors' => $authChannel->errors()->toArray(),
            'resource' => $authChannel->account(),
        ]);
    }
}

Authenticator 类的实例负责查找匹配的可认证通道。

您可以选择不传递通道名称以使用默认通道。

认证门面

您可以定义默认认证通道,并使用 Raid\Core\Auth\Facades\Authentication 门面来处理认证。

config/authentication.php 文件中定义默认认证通道。

'default_channel' => \App\Http\Authentication\Channels\OtpAuthChannel::class,
namespace App\Http\Controllers;

use Raid\Core\Auth\Facades\Authentication;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class AuthController extends Controller
{
    /**
     * Invoke the controller method.
     */
    public function __invoke(Request $request): JsonResponse
    {
        $credentials = $request->only([
            'username', 'password',
        ]);

        $authChannel = Authentication::authenticate(new User(), $credentials);

        return response()->json([
            'channel' => $authChannel->channel(),
            'token' => $authChannel->stringToken(),
            'errors' => $authChannel->errors()->toArray(),
            'resource' => $authChannel->account(),
        ]);
    }
}

Authentication 门面负责处理认证过程。

Authentication 门面使用来自 config/authentication.php 文件的 default_channel


就这些了。

许可证

MIT 许可证 (MIT)。有关更多信息,请参阅许可证文件

鸣谢

安全

如果您发现任何安全相关的问题,请通过电子邮件报告,而不是使用问题跟踪器。

关于 Raid

Raid 是由 Mohamed Khedr 创建的 PHP 框架,并由 Mohamed Khedr 维护。

支持 Raid

Raid 是一个 MIT 许可的开源项目。它是一个独立的项目,其持续发展得以实现。