Add transfer tracking and secure admin access
This commit is contained in:
@@ -1,24 +1,49 @@
|
||||
using MiaoJiZhang.Domain.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace MiaoJiZhang.Api.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 管理后台鉴权:请求头 X-Admin-Key 与 appsettings.Admin:Key 匹配即可。
|
||||
/// 仅内部使用,不依赖 JWT/用户体系。
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public class AdminAuthAttribute : Attribute, IAuthorizationFilter
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
|
||||
public sealed class AdminAuthAttribute(params string[] roles) : Attribute, IAsyncAuthorizationFilter
|
||||
{
|
||||
public void OnAuthorization(AuthorizationFilterContext context)
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
var config = context.HttpContext.RequestServices.GetRequiredService<IConfiguration>();
|
||||
var key = config["Admin:Key"];
|
||||
if (string.IsNullOrWhiteSpace(key) ||
|
||||
!context.HttpContext.Request.Headers.TryGetValue("X-Admin-Key", out var provided) ||
|
||||
provided != key)
|
||||
var request = context.HttpContext.Request;
|
||||
var service = context.HttpContext.RequestServices.GetRequiredService<AdminSessionService>();
|
||||
var authenticated = await service.AuthenticateAsync(
|
||||
context.HttpContext,
|
||||
validateCsrf: !AdminSessionService.IsSafeMethod(request.Method),
|
||||
context.HttpContext.RequestAborted);
|
||||
if (authenticated is null)
|
||||
{
|
||||
context.Result = new UnauthorizedObjectResult(new { error = "admin_key_required", message = "请在 Header 中提供 X-Admin-Key" });
|
||||
context.Result = new UnauthorizedObjectResult(new
|
||||
{
|
||||
error = "admin_session_required",
|
||||
message = "管理会话已失效,请重新登录",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var principal = authenticated.Value.Principal;
|
||||
if (principal.MustChangePassword &&
|
||||
!request.Path.StartsWithSegments("/api/admin/auth"))
|
||||
{
|
||||
context.Result = new ObjectResult(new
|
||||
{
|
||||
error = "password_change_required",
|
||||
message = "首次登录必须修改密码",
|
||||
}) { StatusCode = StatusCodes.Status403Forbidden };
|
||||
return;
|
||||
}
|
||||
if (principal.Role == AdminRoles.Viewer &&
|
||||
!AdminSessionService.IsSafeMethod(request.Method) &&
|
||||
!request.Path.StartsWithSegments("/api/admin/auth"))
|
||||
{
|
||||
context.Result = new ForbidResult();
|
||||
return;
|
||||
}
|
||||
if (roles.Length > 0 && !roles.Contains(principal.Role))
|
||||
context.Result = new ForbidResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user