feat(core): harden sessions settlement and credit activity
This commit is contained in:
@@ -8,6 +8,7 @@ use Illuminate\Support\Str;
|
||||
use App\Support\ApiResponse;
|
||||
use App\Support\AdminAuthProfile;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Services\AdminCaptchaService;
|
||||
@@ -37,38 +38,59 @@ final class LoginController extends Controller
|
||||
|
||||
$normalizedAccount = Str::lower(trim($data['account']));
|
||||
|
||||
/** @var AdminUser|null $admin */
|
||||
$admin = AdminUser::query()->where('username', $normalizedAccount)->first();
|
||||
|
||||
$passwordOk = $admin !== null && Hash::check($data['password'], $admin->password);
|
||||
|
||||
if (! $passwordOk) {
|
||||
/** 统一措辞,弱化枚举用户 */
|
||||
return ApiResponse::error(
|
||||
trans('admin.invalid_credentials', [], $locale),
|
||||
ErrorCode::AdminCredentialsInvalid->value,
|
||||
null,
|
||||
401,
|
||||
);
|
||||
}
|
||||
|
||||
if ((int) $admin->status !== 0) {
|
||||
return ApiResponse::error(
|
||||
trans('admin.account_disabled', [], $locale),
|
||||
ErrorCode::AdminAccountDisabled->value,
|
||||
null,
|
||||
403,
|
||||
);
|
||||
}
|
||||
|
||||
$ttlDays = (int) config('lottery.admin_api.token_ttl_days', 7);
|
||||
$plainToken = $admin->createToken(
|
||||
'admin-api',
|
||||
['*'],
|
||||
now()->addDays(max(1, $ttlDays)),
|
||||
)->plainTextToken;
|
||||
|
||||
$admin->forceFill(['last_login_at' => now()])->save();
|
||||
/**
|
||||
* 串行化同账号登录,确保并发登录也只有版本号最大的会话有效。
|
||||
* 不立即删除旧 Token,旧端下一次请求时才能收到明确的 8115 业务码。
|
||||
*/
|
||||
$loginResult = DB::transaction(function () use ($normalizedAccount, $data, $ttlDays, $locale): array|JsonResponse {
|
||||
/** @var AdminUser|null $lockedAdmin */
|
||||
$lockedAdmin = AdminUser::query()
|
||||
->where('username', $normalizedAccount)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if ($lockedAdmin === null || ! Hash::check($data['password'], $lockedAdmin->password)) {
|
||||
/** 统一措辞,弱化枚举用户 */
|
||||
return ApiResponse::error(
|
||||
trans('admin.invalid_credentials', [], $locale),
|
||||
ErrorCode::AdminCredentialsInvalid->value,
|
||||
null,
|
||||
401,
|
||||
);
|
||||
}
|
||||
|
||||
if ((int) $lockedAdmin->status !== 0) {
|
||||
return ApiResponse::error(
|
||||
trans('admin.account_disabled', [], $locale),
|
||||
ErrorCode::AdminAccountDisabled->value,
|
||||
null,
|
||||
403,
|
||||
);
|
||||
}
|
||||
|
||||
$sessionVersion = (int) $lockedAdmin->admin_session_version + 1;
|
||||
$lockedAdmin->forceFill([
|
||||
'admin_session_version' => $sessionVersion,
|
||||
'last_login_at' => now(),
|
||||
])->save();
|
||||
|
||||
$token = $lockedAdmin->createToken(
|
||||
'admin-api',
|
||||
['admin-session:'.$sessionVersion],
|
||||
now()->addDays(max(1, $ttlDays)),
|
||||
)->plainTextToken;
|
||||
|
||||
return [$token, $lockedAdmin];
|
||||
});
|
||||
|
||||
if ($loginResult instanceof JsonResponse) {
|
||||
return $loginResult;
|
||||
}
|
||||
|
||||
[$plainToken, $admin] = $loginResult;
|
||||
|
||||
return ApiResponse::success([
|
||||
'token' => $plainToken,
|
||||
'token_type' => 'Bearer',
|
||||
|
||||
Reference in New Issue
Block a user