erdemkeren/laravel-otp

使用一次性密码(OTP)来保护您的Laravel路由。

v4.0.0 2021-08-04 19:33 UTC

This package is auto-updated.

Last update: 2024-09-22 10:57:03 UTC


README

需要维护者!

我没有时间维护这个包,因此正在寻找维护者。我对放弃这个项目并建议替代包持开放态度。

Laravel OTP

Latest Version on Packagist Software License Build Status StyleCI Quality Score Code Coverage Total Downloads

此包允许您使用一次性密码(OTP)来保护您的资源。

示例用法

Route::get('secret', function (\Illuminate\Http\Request $request): string {
    $token = $request->otpToken();
    $messages[] = "The otp token {$token} has {$token->timeLeft()} out of {$token->expiryTime()} seconds.";

    $token->refresh();
    $messages[] = "The time left can be reset using the refresh method: {$token->timeLeft()}/{$token->expiryTime()}";

    $token->extend(30);
    $messages[] = "The expiry time can be increased using the extend method: {$token->timeLeft()}/{$token->expiryTime()}";

    $messages[] = "You can also invalidate the token immediately. Try refreshing the page ;)";
    $request->otpToken()->invalidate();

    return implode('<br>', $messages);
})->middleware('auth', 'otp');

内容

安装

1- 将包添加到依赖项。

$ composer require erdemkeren/laravel-otp;

2- 在您的 config/app.php 文件中注册包

仅当您的自动包发现关闭时。

Erdemkeren\Otp\OtpServiceProvider::class,

3- 发布组件

发布一个迁移、两个视图和一个配置文件。

$ php artisan vendor:publish

4- 应用迁移

将创建一个名为 otp_tokens 的表来存储生成的令牌信息。

$ php artisan migrate

5- 注册路由

如果您计划使用 otp 中间件,则需要这些路由。

在您的 RouteServiceProvider 中,在 map 方法内追加以下行

// App\RouteServiceProvider@map:
\Erdemkeren\Otp\OtpRoutes::register();

6- 注册路由中间件

在您的 App\Http\Kernel 中注册 otp 路由中间件。

/**
 * The application's route middleware.
 *
 * These middleware may be assigned to groups or used individually.
 *
 * @var array
 */
protected $routeMiddleware = [
    // [...]
    'otp' => \Erdemkeren\Otp\Http\Middleware\Otp::class,
];

配置

此包提供了一套方便的配置选项

password_generator: 密码生成器选项允许您决定使用哪种生成器实现来生成新密码。

可用内置选项:string, numeric 和 numeric-no-0。默认:string

table: 存储OTP令牌的表的名称。

默认:otp_tokens

expiry_time: 令牌的有效期(分钟)。

默认:15

default_channels: 令牌通知的默认通知通道。

使用

基本用法

配置完包的实例后,您可以使用内置的 otp 中间件别名来保护您的端点

Route::get('secret', function (Request $request): string {
    $request->otpToken()->refresh();

    return 'The secret of immortality';
})->middleware('auth', 'otp');

此中间件将任何未认证的请求重定向到我们在安装过程中注册的 otp/create 端点

  • 将使用配置的密码生成器生成密码。
  • 将通过配置的通知渠道通知认证用户。
  • 用户将看到一个表单来提交他们的密码。
  • 您可以通过修改 resources/views/otp 目录下的 create.blade.php 文件来更改视图的外观。
  • 认证成功后,用户将被重定向回他们最初请求的原始路由。
  • 重定向请求还将包含用户使用的 otpToken() 实例。

高级用法

添加通知渠道方法

如果您不使用 mail 通道,或者您的通知通道期望的方法不同于 mailsms,您可以注册自己的方法,如下所示

// AppServiceProvider::register():
TokenNotification::macro('AcmeSms', function () {
    // $this is TokenNotification class.
    return $this->notification->code;
});

别忘了更改您的配置文件。

使用您自己的密码生成器

要添加自己的密码生成器实现,您可以在 Otp 服务上调用 addPasswordGenerator 方法,如下所示

// AppServiceProvider::register():
app('otp')->addPasswordGenerator('acme', function (int $length): string {
    return 'your_implementation';
});

如果您需要更多的功能,您也可以创建自己的密码生成器类

<?php namespace App\Acme\PasswordGenerators;

use Erdemkeren\Otp\PasswordGeneratorInterface;

class AcmePasswordGenerator implements PasswordGeneratorInterface
{
    /**
     * Generate an acme password with the given length.
     *
     * @param  int    $length
     * @return string
     */
    public function generate(int $length): string
    {
        return 'your implementation';
    }
}

您可以像之前的密码生成器一样注册您的密码生成器

// AppServiceProvider::register():
Otp::addPasswordGenerator('acme', AcmePasswordGenerator::class);

别忘了更改您的配置文件。

为每个可通知项确定OTP通道

Notification 类检查正在通知的 notifiable 中是否存在 otpChannels。如果存在,则调用此方法以确定将使用哪个通知通道来通知可通知项。

深入知识

公共API由两个主要组件组成:OtpService 和通常由服务返回的 Token

OTP服务

如果您计划创建自己的API或基本功能不足以满足您的需求,您可以使用OTP服务API

检查给定令牌的有效性
$isTokenValid = Otp::check($authenticableId, $token);
设置密码生成器
Otp::setPasswordGenerator('string');
为给定用户创建新的令牌
$token = Otp::create(auth()->user(), $length = 6);
// See what can be done with tokens below.
通过给定的明文密码从存储中检索现有令牌
$token = Otp::retrieveByPlainText(auth()->id(), $otpPassword);
// See what can be done with tokens below.
通过给定的密文(令牌)从存储中检索现有令牌
$token = Otp::retrieveByCipherText(auth()->id(), $otpPassword);
// See what can be done with tokens below.
更改服务的行为

该软件包附带一个 ServiceProvider,它将OTP服务注册到您的应用程序容器中。

OTP协调对以下3个接口实现的调用方法。

  • 密码生成器管理接口
  • 加密器接口和
  • 令牌接口

您可以为您的服务提供程序编写代码,并使用您的依赖项版本注册 OtpService

注意:由于令牌类使用静态调用,您必须发送您 TokenInterface 实现的完全限定名称。

令牌API

获取令牌的属性
public function authenticableId();
public function cipherText(): string;
public function plainText(): ?string; // If you have just created the token, plain text will be accessable. If you retrieved it; it won't.
public function createdAt(): Carbon;
public function updatedAt(): Carbon;
public function expiryTime(): int;
public function expiresAt(): Carbon;
public function timeLeft(): int;
public function expired(): bool;
使令牌无效
public function revoke(): void;
public function invalidate(): void;

例如

public function show(Request $request, $id) {
    if($request->input('revoke_session', false)) {
        $request->otpToken()->revoke();
    }

    return view('heaven');
}
扩展或刷新令牌过期时间
// Extend the usage time of the token for the given seconds:
public function extend(?int $seconds = null): bool;
// Make the token function like it has just been created:
public function refresh(): bool;

例如

$token = Otp::retrieveByCipherText(
    auth()->id(),
    $request->input('otp_token')
);

if(! $token->expired()) {
 $token->refresh();
}
创建新的令牌
public static function create(
    $authenticableId,
    string $cipherText,
    ?string $plainText = null
): TokenInterface;

例如

$token = Token::create(1, 'foo', 'plain foo');
通过给定的属性从存储中检索令牌

确保您提供的属性将返回唯一的令牌。

public static function retrieveByAttributes(array $attributes): ?TokenInterface;

例如

$token = Token::retrieveByAttributes([
    'authenticable_id' => 1,
    'cipher_text'      => 'foo',
]);
将令牌转换为通知
public function toNotification(): Notification;

例如

$user->notify($token->toNotification());

变更日志

有关最近更改的更多信息,请参阅 变更日志

测试

$ composer test

致谢

  • Hilmi Erdem KEREN
  • Berkay Güre

SymfonyInsight