Files
2026-11-06 13:37:12 +08:00

45 lines
2.8 KiB
PHP
Executable File

<?php
declare(strict_types=1);
namespace app\adminapi\service\iam\v2;
use RuntimeException;
use think\facade\Db;
final class IamV2Flow
{
public static function put(string $kind, string $secret, array $payload, int $ttl = 300): void
{
Db::name('iam_v2_flow')->insert(['flow_key' => hash('sha256', $kind . ':' . $secret), 'kind' => $kind,
'payload' => json_encode($payload, JSON_THROW_ON_ERROR), 'expires_at' => time() + $ttl, 'consumed' => 0]);
}
public static function peek(string $kind, string $secret, string $sid): array
{
$row = Db::name('iam_v2_flow')->where('flow_key', hash('sha256', $kind . ':' . $secret))->find();
if (!$row || $row['expires_at'] <= time() || (int) $row['consumed'] !== 0) { throw new RuntimeException('TRANSACTION_EXPIRED'); }
$p = json_decode($row['payload'], true, 32, JSON_THROW_ON_ERROR);
if (($p['sessionId'] ?? '') !== $sid) { throw new RuntimeException('ACCOUNT_MISMATCH'); }
return $p;
}
public static function take(string $kind, string $secret, string $sid): array
{
return Db::transaction(function () use ($kind, $secret, $sid) {
$key = hash('sha256', $kind . ':' . $secret);
$row = Db::name('iam_v2_flow')->where('flow_key', $key)->lock(true)->find();
if (!$row || $row['expires_at'] <= time() || (int) $row['consumed'] !== 0) { throw new RuntimeException('TRANSACTION_EXPIRED'); }
$p = json_decode($row['payload'], true, 32, JSON_THROW_ON_ERROR);
if (($p['sessionId'] ?? '') !== $sid) { throw new RuntimeException('ACCOUNT_MISMATCH'); }
if (Db::name('iam_v2_flow')->where('flow_key', $key)->where('consumed', 0)->update(['consumed' => 1]) !== 1) { throw new RuntimeException('TRANSACTION_EXPIRED'); }
return $p;
});
}
public static function form(string $url, array $fields): string
{
$nonce = bin2hex(random_bytes(18));
$escape = fn($s) => htmlspecialchars((string) $s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$inputs = '';
foreach ($fields as $name => $value) { $inputs .= '<input type="hidden" name="' . $escape($name) . '" value="' . $escape($value) . '">'; }
$origin = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . (parse_url($url, PHP_URL_PORT) ? ':' . parse_url($url, PHP_URL_PORT) : '');
return '<!doctype html><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src \'none\'; base-uri \'none\'; form-action ' . $escape($origin) . '; script-src \'nonce-' . $nonce . '\'"><title>正在验证身份</title><form method="post" action="' . $escape($url) . '">' . $inputs . '<button>继续</button></form><script nonce="' . $nonce . '">document.forms[0].submit()</script>';
}
}