72 lines
2.1 KiB
PHP
72 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Player;
|
|
|
|
use Throwable;
|
|
use App\Models\AdminSite;
|
|
use App\Lottery\ErrorCode;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use App\Exceptions\PlayerAuthenticationException;
|
|
|
|
final class NativeJwtSecretGuard
|
|
{
|
|
public function validatedSecret(): string
|
|
{
|
|
$secret = config('lottery.player_auth.native.secret');
|
|
if (! is_string($secret) || $secret === '') {
|
|
$this->rejectConfiguration('原生登录未配置');
|
|
}
|
|
|
|
$legacySsoSecret = config('lottery.main_site.sso_jwt_secret');
|
|
if ($this->matches($secret, $legacySsoSecret)) {
|
|
$this->rejectConfiguration('原生登录密钥不得与 legacy SSO 密钥相同');
|
|
}
|
|
|
|
if ($this->matchesStoredSiteSsoSecret($secret)) {
|
|
$this->rejectConfiguration('原生登录密钥不得与任何站点保存的 SSO 密钥相同');
|
|
}
|
|
|
|
return $secret;
|
|
}
|
|
|
|
private function matchesStoredSiteSsoSecret(string $nativeSecret): bool
|
|
{
|
|
try {
|
|
if (! Schema::hasTable('admin_sites')
|
|
|| ! Schema::hasColumn('admin_sites', 'sso_jwt_secret_encrypted')) {
|
|
return false;
|
|
}
|
|
|
|
$sites = AdminSite::query()
|
|
->whereNotNull('sso_jwt_secret_encrypted')
|
|
->get(['sso_jwt_secret_encrypted']);
|
|
} catch (Throwable) {
|
|
$this->rejectConfiguration('无法检查原生登录密钥是否与站点 SSO 密钥冲突');
|
|
}
|
|
|
|
foreach ($sites as $site) {
|
|
if ($this->matches($nativeSecret, $site->decryptedSsoJwtSecret())) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private function matches(string $nativeSecret, mixed $candidate): bool
|
|
{
|
|
return is_string($candidate)
|
|
&& $candidate !== ''
|
|
&& hash_equals($nativeSecret, $candidate);
|
|
}
|
|
|
|
private function rejectConfiguration(string $message): never
|
|
{
|
|
throw new PlayerAuthenticationException(
|
|
$message,
|
|
ErrorCode::PlayerSsoSecretNotConfigured->value,
|
|
503,
|
|
);
|
|
}
|
|
}
|