登录验证码接入
This commit is contained in:
1 parent
ae075bda6d
commit
f489a3553c
15 files changed
+471
-19
No files matched your search
@@ -20,6 +20,7 @@ public sealed class AuthController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
IAuthSessionService authSessionService,
|
||||
ITwoFactorLoginTicketService twoFactorTickets,
|
||||
ILoginCaptchaService loginCaptcha,
|
||||
IAppCache cache) : ControllerBase
|
||||
{
|
||||
[AllowAnonymous]
|
||||
@@ -135,6 +136,35 @@ public sealed class AuthController(
|
||||
});
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[EnableRateLimiting("public-auth")]
|
||||
[HttpPost("login/captcha")]
|
||||
public async Task<ActionResult<LoginCaptchaChallenge>> CreateLoginCaptcha(
|
||||
LoginCaptchaCreateRequest request,
|
||||
CancellationToken cancellationToken) =>
|
||||
Ok(await loginCaptcha.CreateAsync(request.DeviceId, HttpContext, cancellationToken));
|
||||
|
||||
[AllowAnonymous]
|
||||
[EnableRateLimiting("public-auth")]
|
||||
[HttpPost("login/captcha/verify")]
|
||||
public async Task<ActionResult<LoginCaptchaProofResponse>> VerifyLoginCaptcha(
|
||||
LoginCaptchaVerifyRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var proof = await loginCaptcha.VerifyAsync(
|
||||
new LoginCaptchaVerification(
|
||||
request.ChallengeId,
|
||||
request.DeviceId,
|
||||
request.Text,
|
||||
request.SliderX,
|
||||
request.Clicks),
|
||||
HttpContext,
|
||||
cancellationToken);
|
||||
return proof is null
|
||||
? Unauthorized(LoginCaptchaProblem())
|
||||
: Ok(new LoginCaptchaProofResponse(proof));
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[EnableRateLimiting("public-auth")]
|
||||
[HttpPost("login")]
|
||||
@@ -142,6 +172,12 @@ public sealed class AuthController(
|
||||
LoginRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!await loginCaptcha.ConsumeProofAsync(
|
||||
request.CaptchaTicket,
|
||||
request.DeviceId,
|
||||
HttpContext,
|
||||
cancellationToken))
|
||||
return Unauthorized(LoginCaptchaProblem());
|
||||
var user = await userManager.FindByNameAsync(request.UserName);
|
||||
if (user is null || !user.IsEnabled)
|
||||
{
|
||||
@@ -290,6 +326,12 @@ public sealed class AuthController(
|
||||
Status = StatusCodes.Status401Unauthorized
|
||||
};
|
||||
|
||||
internal static ProblemDetails LoginCaptchaProblem() => new()
|
||||
{
|
||||
Title = "安全验证失败", Detail = "验证码无效、已过期或与当前设备不匹配,请重新验证。",
|
||||
Status = StatusCodes.Status401Unauthorized
|
||||
};
|
||||
|
||||
internal static LoginResponse CreateLoginResponse(AuthSessionResult session) =>
|
||||
new(
|
||||
session.AccessToken,
|
||||
@@ -308,8 +350,21 @@ public sealed class AuthController(
|
||||
public sealed record LoginRequest(
|
||||
[Required, MaxLength(100)] string UserName,
|
||||
[Required, MaxLength(100)] string Password,
|
||||
[Required, MinLength(32), MaxLength(128)] string CaptchaTicket,
|
||||
[MaxLength(128)] string? DeviceId = null,
|
||||
bool IsNativeApp = false);
|
||||
|
||||
public sealed record LoginCaptchaCreateRequest([MaxLength(128)] string? DeviceId = null);
|
||||
|
||||
public sealed record LoginCaptchaVerifyRequest(
|
||||
[Required, MinLength(32), MaxLength(64)] string ChallengeId,
|
||||
[MaxLength(128)] string? DeviceId,
|
||||
[MaxLength(16)] string? Text,
|
||||
[Range(0, 240)] int? SliderX,
|
||||
[MaxLength(2)] IReadOnlyList<CaptchaPoint>? Clicks);
|
||||
|
||||
public sealed record LoginCaptchaProofResponse(string CaptchaTicket);
|
||||
|
||||
public sealed record TotpLoginRequest(
|
||||
[Required, MinLength(20), MaxLength(2048)] string TwoFactorTicket,
|
||||
[Required, MinLength(6), MaxLength(12)] string Code);
|
||||
|
||||
Reference in new issue
Block a user