mmockelyn/laravel-otp

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

v4.0.1 2022-03-15 11:58 UTC

This package is not auto-updated.

Last update: 2024-09-25 22:28:43 UTC


README

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 Service

如果您计划创建自己的API或基本功能不足以满足您的需求,您可以使用Otp Service 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个接口实现进行的调用。

  • 密码生成器管理接口(PasswordGeneratorManagerInterface)
  • 加密器接口(EncryptorInterface)和
  • 令牌接口(TokenInterface)

您可以编写自己的服务提供程序,并使用您的依赖版本注册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