laragear/token-action

使用令牌接受或拒绝操作,限制使用次数。

v1.2.1 2024-09-05 06:54 UTC

This package is auto-updated.

Last update: 2024-09-05 06:55:10 UTC


README

Latest Version on Packagist Latest stable test run Codecov coverage Maintainability Sonarcloud Status Laravel Octane Compatibility

使用令牌接受或拒绝操作,限制使用次数。

use Laragear\TokenAction\Facades\Token;

$token = Token::until('tomorrow');

return "Confirm the invite using this code: $token";

用于一次性操作,例如确认邀请或投票,消耗后不再有效。

保持此包免费

您的支持使我能够保持此包免费、更新和可维护。或者,您也可以传播消息!

安装

启动 Composer 并在项目中要求此包。

compose require laragear/token-action

就这样。

它是如何工作的

令牌通过随机生成的密钥和默认前缀保存在您的应用程序缓存中。

默认情况下,Token Action 将使用默认的 Laravel 缓存。您可以使用 TOKEN_ACTION_STORE 环境变量设置自定义缓存,例如,使用持久缓存(如 databasefileredis)而不是临时缓存(如 memcachearray)。

TOKEN_ACTION_STORE=file

令牌有一定的“尝试”次数。当令牌尝试次数达到 0 时,它将从缓存中删除。

创建令牌

可以使用 Token 面具的 until() 方法创建令牌,以及令牌应该过期的时间点。您可以使用分钟数、\DateTimeInterface(如 Carbon 实例)或字符串进行解析。

use Laragear\TokenAction\Facades\Token;

// Create a token for 10 minutes
$token = Token::until(10);

// Create a token for tomorrow
$token = Token::until('tomorrow');

// Create a token for a specific moment of time
$token = Token::until(now()->addHour());

您将收到一个 Laragear\TokenAction\Token 实例,它已经持久化到数据库中,具有随机的 ID,可通过 id 属性访问。

您可以使用 ID 字符串,例如,将其发送到电子邮件或将其作为 URL 参数的一部分。令牌实例可以转换为 ID 字符串,因此如果您需要,可以安全地将其输出为文本。

use Laragear\TokenAction\Facades\Token;

$token = Token::until('tomorrow');

return route('party.invite', [
    'party' => 420,
    'user_id' => 10,
    'token' => (string) $token,
]);

// https://myapp.com/party/420/invite?user_id=10&token=e9f83d...

多用途令牌

令牌默认为单次使用,但您可以使用 tries() 方法以及尝试次数创建可以多次使用的令牌。

use Laragear\TokenAction\Facades\Token;

$token = Token::tries(2)->until('tomorrow');

令牌以后可以多次使用

有效负载

令牌可以通过 with() 方法保存有效负载,如数组或字符串。

use Laragear\TokenAction\Facades\Token;

$token = Token::with(['cool' => true])->until('tomorrow');

您还可以添加 Eloquent 模型或 Eloquent 集合作为有效负载。这些将使用它们的主键进行序列化,而不包括关系,以避免违反缓存大小限制。

use Laragear\TokenAction\Facades\Token;
use App\Models\Tour;
use App\Models\Party;

// Use a single model
$token = Token::with(Party::find(420))->until('tomorrow');

// Use a collection
$token = Token::with(Tour::limit(10)->get())->until('tomorrow');

在您检索令牌后,有效负载将作为 payload 属性包含在内。

use Laragear\TokenAction\Facades\Token;

$token = Token::find('e9f83d...');

if ($token) {
    $party = $token->payload->party->name;
    
    return "You confirmed the invitation to $party";
}

return 'You need to be invited the first place.';

重要

令牌有效负载为只读。如果需要更改有效负载,请考虑克隆数据。

检索令牌

使用令牌最直接的方式是调用 Token 面具的 consume() 方法。如果令牌 ID 存在、未过期且至少还有 1 次尝试剩余,它将作为 Token 实例返回,否则将返回 null

use Laragear\TokenAction\Facades\Token;

$token = Token::consume('e9f83d...');

if ($token) {
    return 'You are confirmed your invitation to the party!'.
}

如果您想当令牌未找到时失败,请使用 consumeOrFail(),它将返回 HTTP 404(未找到)异常。

use Laragear\TokenAction\Facades\Token;

Token::consumeOrFail('e9f83d...');

return 'You are confirmed your invitation to the party #420';

查找令牌

如果您需要检索一个不消耗它的令牌,请使用 Token 门面的 find() 方法。如果令牌存在且有尝试次数,您将收到一个 Laragear\TokenAction\Token 实例,否则将返回 null

检索令牌后,您应该使用 consume() 方法实际消耗令牌。如果令牌有多个尝试次数,消耗一次将尝试次数减一。

use Laragear\TokenAction\Facades\Token;

$token = Token::find('e9f83d...');

if ($token?->consume()) {
    return 'Assistance confirmed!';
}

return 'You are not invited';

如果您想通过返回 HTTP 404(未找到)异常来查找令牌或失败,请使用带令牌 ID 的 findOrFail() 方法。

use Laragear\TokenAction\Facades\Token;

$token = Token::findOrFail('e9f83d...');

$token->consume();

return 'Assistance confirmed!';

路由绑定

如果您想在路由操作中检索令牌,请使用 token 作为路由键来绑定它。在您的路由操作中,您应该将 Token 类型提示为 $token

与模型一样,如果令牌不存在或已过期,将抛出 HTTP 404(未找到)异常。

use App\Models\Party;
use Illuminate\Support\Facades\Route;
use Laragear\TokenAction\Token;

Route::get('party/{party}/invitation/{token}', function (Party $party, Token $token) {
    return view('party.confirm')->with([
        'party' => $party,
        'token' => $token,
    ]);
})

如果 token 路由键已被您的应用程序使用,您可以在配置中 更改

删除令牌

删除令牌的唯一方法是知道它的 ID。如果您有一个 Token 实例,您可以使用 delete() 方法。

use Laragear\TokenAction\Facades\Token;

$token = Token::find('e9f83d...');

if ($token) {
    $token->delete();
}

您也可以使用 Token 门面的 destroy() 方法使用令牌的 ID。

use Laragear\TokenAction\Facades\Token;

Token::destroy('e9f83d...');

缓存存储

您可以使用 store() 方法在运行时更改用于管理令牌的缓存存储。

use Laragear\TokenAction\Facades\Token;

$partyToken = Token::store('redis')->find('e9f83d...');

$dateToken = Token::store('memcached')->find('e9f83d...');

或者,您可以选择更改要使用的 默认缓存存储

中间件

此包包含两个中间件,分别是 token.validatetoken.consume

token.validate 中间件检查令牌是否存在,但不会消耗它。这对于例如在令牌有足够的尝试次数且未过期的情况下显示表单非常有用。

use Illuminate\Support\Facades\Route;
use App\Models\Party;

// Show the form to confirm the invitation.
Route::get('{party}/confirm', function (Party $party) {
    return view('party.confirm')->with('party', $party);
})->middleware('token.validate');

另一方面,token.consume 中间件在返回成功响应后自动消耗来自 URL 参数的令牌。换句话说,如果响应成功(HTTP 2XX)或重定向(HTTP 3XX),则消耗令牌。

这可以在例如接收前端表单提交时使用。

use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
use App\Models\Party;

// Show the form to confirm the invitation.
Route::get('{party}/confirm', function (Party $party) {
    // ...
})->middleware('token.validate');

// Handle the form submission.
Route::post('{party}/confirm', function (Request $request, Party $party) {
	$party->confirmInviteForUser($request->user());
	
	return back();
})->middleware('token.consume');

重要

这些中间件仅在 查询 URL 上工作。如果您将令牌设置在查询 URL 之外,您应该在路由操作中手动检查。

令牌参数键

token.validatetoken.consume 中间件都尝试在 token URL 参数中查找令牌。如果令牌位于另一个键中,您可以将它作为参数指出。

use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
use App\Models\Party;

Route::get('{party}/confirm', function (Party $party) {
	// ...
})->middleware('token.validate:token-action');

Route::post('{party}/confirm', function (Request $request, Party $party) {
	// ...
})->middleware('token.consume:token-action');

多次消耗

当使用 token.consume 中间件时,令牌将被消耗正好 1 次。您可以通过设置一个数字作为最后一个中间件参数来更改此设置。

use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
use App\Models\Party;

Route::post('{party}/confirm', function (Request $request, Party $party) {
	// ...
})->middleware('token.consume:2');

ID 生成器

默认情况下,令牌 ID 是由 Str::ulid() 创建的 ULID。您可以在运行时使用 as() 方法将其更改为任何内容。它接受一个字符串,或一个返回字符串的闭包。

use Laragear\TokenAction\Facades\Token;
use Illuminate\Support\Str;

$token = Token::store('redis')->as(Str::random(32))->until(60);

或者,您可以使用 Token::$generator 静态属性和您的 AppServiceProviderboot() 方法的闭包,该闭包返回一个随机字符串。

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Laragear\TokenAction\Token;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Token::$generator = fn() => Str::random(48);
    }
}

注意

要生成字符串将 前缀 以避免缓存键冲突。

配置

要进一步配置此包,发布配置文件

php artisan vendor:publish --provider="Laragear\TokenAction\TokenActionServiceProvider" --tag="config"

您将收到具有以下内容的 config/token-action.php 配置文件

return [
    'default' => env('TOKEN_ACTION_STORE'),
    'prefix' => env('TOKEN_ACTION_PREFIX', 'token-action'),
    'middleware' => [
        'validate' => 'token.validate',
        'consume' => 'token.consume',
    ],
    'bound' => 'token',
]

缓存

return [
    'default' => env('TOKEN_ACTION_STORE'),
    'prefix' => env('TOKEN_ACTION_PREFIX', 'token-action'),

default 键设置应用程序中要使用的哪个缓存存储。如果没有设置,它将使用应用程序中设置的默认值,对于新安装通常是 file

库生成的令牌的所有键使用 prefix 字符串作为前缀。

不要直接在配置文件中更改这些值,而是应该分别使用环境变量 TOKEN_ACTION_STORETOKEN_ACTION_PREFIX

TOKEN_ACTION_STORE=memcache
TOKEN_ACTION_PREFIX=my-custom-prefix

重要

请确保为您的令牌设置一个容错和持久的缓存。使用易失性缓存存储会在令牌仍应有效时修剪旧令牌。一个好的选择是 file 存储,但您可以使用 database 以获得最大的可靠性,或者使用与 持久化 兼容的 redis 存储。

中间件别名

return [
    'middleware' => [
        'validate' => 'token.validate',
        'consume' => 'token.consume',
    ],
]

这两个中间件别名 都在此处配置。如果您有其他具有相同别名的中间件,您也可以在此处更改它们。或者,您始终可以通过指定中间件类来在路由中设置中间件。

use Illuminate\Support\Facades\Route;
use Laragear\TokenAction\Http\Middleware\TokenValidateMiddleware;

Route::get('invite', function () {
    // ...
})->middleware(TokenValidateMiddleware::class)

路由绑定键

return [
    'bound' => 'token',
]

此库将 token 字符串注册为路由键,以根据接收到的字符串 ID 创建 Token 实例。虽然这通常不会带来问题,但您可能已经有一个模型或另一个库正在使用该路由键为其自己的类。在此,您可以将其更改为非冲突的键,如 tokenAction

Laravel Octane 兼容性

  • 唯一使用过时应用程序实例的单例是 Token Store。
  • 没有使用过时配置实例的单例。
  • 没有使用过时请求实例的单例。
  • 在请求过程中没有写入静态属性。

Token Store 以及其内部的缓存存储实例在应用程序生命周期内不应更改。

除此之外,使用此包与 Laravel Octane 应该没有问题。

安全

如果您发现任何安全问题,请通过电子邮件 darkghosthunter@gmail.com 而不是使用问题跟踪器。

令牌交换

用户可以在 URL 中用有效的令牌交换无效的令牌以绕过令牌验证。为了避免这种情况,您可以选择以下方式:

根据使用的 Token 动作,一种可能比另一种更好。例如,如果您预期请求量很高,签名路由可能很好,因为它不会击中应用程序缓存或数据库。另一方面,如果需要复杂或私有信息,这些信息不适合 URL 查询且始终需要正确数据,则令牌有效载荷可能是一个很好的解决方案。

许可证

此特定包版本在发布时受 MIT 许可证 的条款约束。

LaravelTaylor Otwell 的商标。版权所有 © 2011-2024 Laravel LLC。