feat(admin): 增加玩家密码管理与角色边界
This commit is contained in:
@@ -2,11 +2,10 @@
|
||||
|
||||
namespace App\Services\Player;
|
||||
|
||||
use App\Lottery\ErrorCode;
|
||||
use App\Models\Player;
|
||||
use App\Support\PlayerAuthSource;
|
||||
use Firebase\JWT\JWT;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Player;
|
||||
use App\Lottery\ErrorCode;
|
||||
use App\Support\PlayerAuthSource;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Exceptions\PlayerAuthenticationException;
|
||||
|
||||
@@ -103,6 +102,7 @@ final class PlayerNativeAuthService
|
||||
$payload = [
|
||||
$playerIdKey => (int) $player->id,
|
||||
$authKey => PlayerAuthSource::LOTTERY_NATIVE,
|
||||
'token_version' => (int) ($player->native_token_version ?? 0),
|
||||
'site_code' => (string) $player->site_code,
|
||||
'iat' => $now,
|
||||
'exp' => $now + $ttl,
|
||||
|
||||
64
app/Services/Player/PlayerPasswordService.php
Normal file
64
app/Services/Player/PlayerPasswordService.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Player;
|
||||
|
||||
use App\Models\Player;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
final class PlayerPasswordService
|
||||
{
|
||||
public function change(Player $player, string $currentPassword, string $newPassword): Player
|
||||
{
|
||||
return DB::transaction(function () use ($player, $currentPassword, $newPassword): Player {
|
||||
$locked = Player::query()->lockForUpdate()->findOrFail($player->id);
|
||||
$this->assertPasswordManagedLocally($locked);
|
||||
|
||||
if (! is_string($locked->password_hash) || ! Hash::check($currentPassword, $locked->password_hash)) {
|
||||
throw ValidationException::withMessages([
|
||||
'current_password' => ['current_password_invalid'],
|
||||
]);
|
||||
}
|
||||
|
||||
if (Hash::check($newPassword, $locked->password_hash)) {
|
||||
throw ValidationException::withMessages([
|
||||
'password' => ['new_password_must_differ'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->persist($locked, $newPassword);
|
||||
});
|
||||
}
|
||||
|
||||
public function reset(Player $player, string $newPassword): Player
|
||||
{
|
||||
return DB::transaction(function () use ($player, $newPassword): Player {
|
||||
$locked = Player::query()->lockForUpdate()->findOrFail($player->id);
|
||||
$this->assertPasswordManagedLocally($locked);
|
||||
|
||||
return $this->persist($locked, $newPassword);
|
||||
});
|
||||
}
|
||||
|
||||
private function assertPasswordManagedLocally(Player $player): void
|
||||
{
|
||||
if (! $player->isLotteryNative()) {
|
||||
throw ValidationException::withMessages([
|
||||
'password' => ['native_password_unavailable'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function persist(Player $player, string $newPassword): Player
|
||||
{
|
||||
$player->forceFill([
|
||||
'password_hash' => Hash::make($newPassword),
|
||||
'native_token_version' => (int) $player->native_token_version + 1,
|
||||
'login_failed_count' => 0,
|
||||
'login_locked_until' => null,
|
||||
])->save();
|
||||
|
||||
return $player->refresh();
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ use App\Models\Player;
|
||||
use App\Lottery\ErrorCode;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Support\PlayerAuthSource;
|
||||
use App\Support\PlayerAutoRegistrationDefaults;
|
||||
use App\Support\PlayerFundingMode;
|
||||
use App\Support\PlayerTokenAesUnwrap;
|
||||
use Illuminate\Database\QueryException;
|
||||
use App\Support\PlayerAutoRegistrationDefaults;
|
||||
use App\Exceptions\PlayerAuthenticationException;
|
||||
use App\Services\Integration\PartnerSiteConfigResolver;
|
||||
|
||||
@@ -192,6 +192,11 @@ final class PlayerTokenResolver
|
||||
throw new PlayerAuthenticationException('玩家不存在', ErrorCode::PlayerNotRegistered->value);
|
||||
}
|
||||
|
||||
$tokenVersion = (int) data_get($claims, 'token_version', 0);
|
||||
if ($tokenVersion !== (int) ($player->native_token_version ?? 0)) {
|
||||
throw new PlayerAuthenticationException('Token 已因密码变更失效', ErrorCode::PlayerTokenInvalid->value);
|
||||
}
|
||||
|
||||
$player->forceFill(['last_login_at' => now()])->save();
|
||||
|
||||
return $player->refresh();
|
||||
@@ -288,9 +293,6 @@ final class PlayerTokenResolver
|
||||
return is_string($decoded) ? $decoded : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $claims
|
||||
*/
|
||||
private function assertNativeJwtTemporalPolicy(object $claims): void
|
||||
{
|
||||
if (! isset($claims->exp) || ! is_numeric($claims->exp)) {
|
||||
|
||||
Reference in New Issue
Block a user