sadiqsalau/laravel-otp

Laravel OTP 包

v1.0.6 2024-03-20 21:20 UTC

This package is auto-updated.

Last update: 2024-09-20 22:43:19 UTC


README

介绍

基于类的系统实现的 Laravel OTP 包。每个 OTP 都是一个执行特定操作的类。例如,EmailVerificationOtp 将标记账户为已验证。

安装

通过 composer 安装

composer require sadiqsalau/laravel-otp

发布配置文件

php artisan vendor:publish --provider="SadiqSalau\LaravelOtp\OtpServiceProvider"

使用方法

生成 OTP

php artisan make:otp {name}

新的 OTP 类将被生成到 app/Otp 目录中。例如

php artisan make:otp UserRegistrationOtp

每个 OTP 必须实现一个 process 方法,该方法在验证后将被调用。在那里 OTP 可以执行必要的操作并返回任何结果。

<?php

namespace App\Otp;

use SadiqSalau\LaravelOtp\Contracts\OtpInterface as Otp;

use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

use App\Models\User;

class UserRegistrationOtp implements Otp
{
    /**
     * Constructs Otp class
     */
    public function __construct(
        public string $name,
        public string $email,
        public string $password
    ) {
        //
    }

    /**
     * Processes the Otp
     *
     * @return User
     */
    public function process()
    {
        /** @var User */
        $user = User::unguarded(function () {
            return User::create([
                'name'                  => $this->name,
                'email'                 => $this->email,
                'password'              => Hash::make($this->password),
                'email_verified_at'     => now(),
            ]);
        });

        event(new Registered($user));

        Auth::login($user);

        return $user;
    }
}

发送 OTP

<?php
use SadiqSalau\LaravelOtp\Facades\Otp;

Otp::identifier($identifier)->send($otp, $notifiable);
  • $otp: 要发送的 OTP。
  • $notifiable: 匿名通知或通知实例。
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Notification;
use Illuminate\Http\Request;
use Illuminate\Validation\Rules;

use SadiqSalau\LaravelOtp\Facades\Otp;

use App\Models\User;
use App\Otp\UserRegistrationOtp;

Route::post('/register', function(Request $request){
    $request->validate([
        'name'          => ['required', 'string', 'max:255'],
        'email'         => ['required', 'string', 'email', 'max:255', 'unique:' . User::class],
        'password'      => ['required',  Rules\Password::defaults()],
    ]);

    $otp = Otp::identifier($request->email)->send(
        new UserRegistrationOtp(
            name: $request->name,
            email: $request->email,
            password: $request->password
        ),
        Notification::route('mail', $request->email)
    );

    return __($otp['status']);
});

返回值

['status' => Otp::OTP_SENT] // Success: otp.sent

验证 OTP

<?php
use SadiqSalau\LaravelOtp\Facades\Otp;

Otp::identifier($identifier)->attempt($code);
  • $code: 要比较的 OTP 代码。

返回值

['status' => Otp::OTP_EMPTY]        // Error: otp.empty
['status' => Otp::OTP_MISMATCHED]  // Error: otp.mismatched
['status' => Otp::OTP_PROCESSED, 'result'=>[]] // Success: otp.processed

result 键包含 OTP 类的 process 方法的返回值

<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;

use SadiqSalau\LaravelOtp\Facades\Otp;

Route::post('/otp/verify', function (Request $request) {

    $request->validate([
        'email'    => ['required', 'string', 'email', 'max:255'],
        'code'     => ['required', 'string']
    ]);

    $otp = Otp::identifier($request->email)->attempt($request->code);

    if($otp['status'] != Otp::OTP_PROCESSED)
    {
        abort(403, __($otp['status']));
    }

    return $otp['result'];
});

不清理缓存验证 OTP

<?php
use SadiqSalau\LaravelOtp\Facades\Otp;

Otp::identifier($identifier)->check($code);
  • $code: 要比较的 OTP 代码。

返回值

['status' => Otp::OTP_EMPTY]        // Error: otp.empty
['status' => Otp::OTP_MISMATCHED]  // Error: otp.mismatched
['status' => Otp::OTP_MATCHED] // Success: otp.matched

重新发送 OTP

<?php
use SadiqSalau\LaravelOtp\Facades\Otp;

Otp::identifier($identifier)->update();

返回值

['status' => Otp::OTP_EMPTY]    // Error: otp.empty
['status' => Otp::OTP_SENT]     // Success: otp.sent
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;

use SadiqSalau\LaravelOtp\Facades\Otp;

Route::post('/otp/resend', function (Request $request) {

    $request->validate([
        'email'    => ['required', 'string', 'email', 'max:255']
    ]);

    $otp = Otp::identifier($request->email)->update();

    if($otp['status'] != Otp::OTP_SENT)
    {
        abort(403, __($otp['status']));
    }
    return __($otp['status']);
});

设置标识符

OTP 类的每个方法都需要设置一个标识符以唯一标识 OTP。

<?php
use SadiqSalau\LaravelOtp\Facades\Otp;

Otp::identifier($request->email)->send(...);
<?php
use SadiqSalau\LaravelOtp\Facades\Otp;

Otp::identifier($identifier)->send(...);
Otp::identifier($identifier)->attempt(...);
Otp::identifier($identifier)->update();
Otp::identifier($identifier)->check(...);

配置

发布包后,配置文件可在 config/otp.php 中找到

  • format - 生成的 OTP 代码的格式 (numeric | alphanumeric | alpha)
  • length - 生成的 OTP 代码的长度
  • expires - OTP 过期前的分钟数
  • notification - 要使用的自定义通知类,默认为 SadiqSalau\LaravelOtp\OtpNotification

翻译

该包不提供开箱即用的翻译,但这里有一个示例。创建一个新的翻译文件:lang/en/otp.php

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | OTP Language Lines
    |--------------------------------------------------------------------------
    |
    | The following language lines are used by the OTP broker
    |
    */

    'sent'          => 'We have sent your OTP code!',
    'empty'         => 'No OTP!',
    'matched'       => 'OTP code verified!',
    'mismatched'    => 'Mismatched OTP code!',
    'processed'     => 'OTP was successfully processed!'
];

然后翻译状态

return __($otp['status'])

API

  • Otp::identifier(mixed $identifier) - 设置 OTP 标识符

  • Otp::send(OtpInterface $otp, mixed $notifiable) - 向通知发送 OTP

  • Otp::attempt(string $code) - 尝试 OTP 代码,返回调用 OTP 的 process 方法的结果

  • Otp::check(string $code) - 将代码与当前 OTP 进行比较,这不会处理或清除 OTP

  • Otp::update() - 重新发送并更新当前 OTP

  • Otp::clear() - 删除 OTP

  • Otp::useGenerator(callable $callback) - 设置要使用的自定义生成器,生成器将使用 $format$length 调用

  • Otp::generateOtpCode($format, $length) - 生成 OTP 代码

贡献

欢迎贡献。