feat(core): harden sessions settlement and credit activity
This commit is contained in:
@@ -6,7 +6,9 @@ use Firebase\JWT\JWT;
|
||||
use App\Models\Player;
|
||||
use App\Lottery\ErrorCode;
|
||||
use App\Support\PlayerAuthSource;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Events\PlayerSessionReplacedBroadcast;
|
||||
use App\Exceptions\PlayerAuthenticationException;
|
||||
|
||||
final class PlayerNativeAuthService
|
||||
@@ -63,17 +65,62 @@ final class PlayerNativeAuthService
|
||||
);
|
||||
}
|
||||
|
||||
$player->forceFill([
|
||||
'login_failed_count' => 0,
|
||||
'login_locked_until' => null,
|
||||
'last_login_at' => now(),
|
||||
])->save();
|
||||
|
||||
$ttl = (int) config('lottery.player_auth.native.ttl_seconds', 28800);
|
||||
$token = $this->issueToken($player, $ttl);
|
||||
|
||||
/** @var array{token: string, player: Player, session_version: int} $login */
|
||||
$login = DB::transaction(function () use ($player, $password, $ttl): array {
|
||||
$locked = Player::query()->lockForUpdate()->find($player->id);
|
||||
if ($locked === null
|
||||
|| ! $locked->isLotteryNative()
|
||||
|| ! is_string($locked->password_hash)
|
||||
|| ! Hash::check($password, $locked->password_hash)) {
|
||||
throw new PlayerAuthenticationException(
|
||||
'账号或密码错误',
|
||||
ErrorCode::PlayerCredentialsInvalid->value,
|
||||
);
|
||||
}
|
||||
|
||||
if ($locked->login_locked_until !== null && $locked->login_locked_until->isFuture()) {
|
||||
throw new PlayerAuthenticationException(
|
||||
'登录已锁定',
|
||||
ErrorCode::PlayerLoginLocked->value,
|
||||
403,
|
||||
);
|
||||
}
|
||||
|
||||
if ((int) $locked->status !== 0) {
|
||||
throw new PlayerAuthenticationException(
|
||||
'账号已冻结',
|
||||
ErrorCode::PlayerAccountSuspended->value,
|
||||
403,
|
||||
);
|
||||
}
|
||||
|
||||
$sessionVersion = (int) ($locked->native_session_version ?? 0) + 1;
|
||||
$locked->forceFill([
|
||||
'login_failed_count' => 0,
|
||||
'login_locked_until' => null,
|
||||
'last_login_at' => now(),
|
||||
'native_session_version' => $sessionVersion,
|
||||
])->save();
|
||||
|
||||
return [
|
||||
'token' => $this->issueToken($locked, $ttl),
|
||||
'player' => $locked->refresh(),
|
||||
'session_version' => $sessionVersion,
|
||||
];
|
||||
});
|
||||
|
||||
event(new PlayerSessionReplacedBroadcast(
|
||||
(int) $login['player']->id,
|
||||
$login['session_version'],
|
||||
(int) floor(microtime(true) * 1000),
|
||||
));
|
||||
|
||||
$player = $login['player'];
|
||||
|
||||
return [
|
||||
'access_token' => $token,
|
||||
'access_token' => $login['token'],
|
||||
'expires_in' => $ttl,
|
||||
'token_type' => 'Bearer',
|
||||
'player' => [
|
||||
@@ -100,6 +147,7 @@ final class PlayerNativeAuthService
|
||||
$playerIdKey => (int) $player->id,
|
||||
$authKey => PlayerAuthSource::LOTTERY_NATIVE,
|
||||
'token_version' => (int) ($player->native_token_version ?? 0),
|
||||
'session_version' => (int) ($player->native_session_version ?? 0),
|
||||
'site_code' => (string) $player->site_code,
|
||||
'iat' => $now,
|
||||
'exp' => $now + $ttl,
|
||||
|
||||
Reference in New Issue
Block a user