在 ASP.NET Core 8 中自定义身份

问题描述 投票:0回答:1

我正在尝试向我的身份用户添加一些附加字段。 我发现通过创建一个继承自 IdentityUser 的新类并将继承的类传递给适当的构建器,这是相当简单的。

没关系,用户表有附加字段。但是,我正在努力找出如何自定义路由“/register”的请求正文,因为那里只请求电子邮件和密码。通常,我会在控制器类中调整这些内容,但 IdentityController 是一个我无权访问的黑匣子。

PS:我没有使用 Blazor。我纯粹使用 ASP.Net 作为后端。 PS2:我正在使用 Identity Core 8 升级非 Identity ASP.Net Core 7 项目

目前我得到的所有信息均来自官方文档: https://learn.microsoft.com/en-us/aspnet/core/migration/identity?view=aspnetcore-8.0 但他们总是提到他们的自定义 UI,这不是我想要的。

这是我的自定义用户类别

public class AuthUser: IdentityUser
{
    public static readonly string KEY_PREFIX = "user";

    // Would like to have a custom id generator but haven't 
    // figured out how to do that either
    // [Key]
    // public string Id { get; set; } = string.Empty;
    [PersonalData]
    [Required]
    public string FirstName { get; set; } = string.Empty;
    [PersonalData]
    [Required]
    public string LastName { get; set; } = string.Empty;
}

数据库类:

public class ApiDbContext: IdentityDbContext<AuthUser>
{
    public ApiDbContext(DbContextOptions<ApiDbContext> options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }

    /* ... */
}

这是我的 Program.cs 的一些内容

/* ... */

builder.Services.AddAuthorization();

// Setup Database
builder.Services.AddDbContext<ApiDbContext>(options => options.UseNpgsql(conn));

// Setup Auth
builder.Services.AddIdentityApiEndpoints<AuthUser>()
    .AddEntityFrameworkStores<ApiDbContext>();
builder.Services.AddAuthentication(IdentityConstants.BearerScheme);

/* ... */

app.MapIdentityApi<AuthUser>();

// Not sure if this part is needed
app.UseAuthorization();
app.UseAuthentication();

/* ... */
asp.net-core asp.net-identity
1个回答
0
投票

我正在努力找出如何自定义请求正文 路由“/register”,因为那里只有电子邮件和密码 要求。通常,我会在我的控制器类中调整这些东西 但 IdentityController 是一个我无权访问的黑匣子。

如果我没理解错的话,您正在尝试实现一个场景,您可以选择根据请求路径或特定页面来验证

AuthUser
类属性。

嗯,这可以通过多种方式实现。例如,您可以使用 HttpContextAccessor 覆盖 IActionFilter 并获取请求路由或操作名称,甚至请求路径,以检查您的请求来源。

然后您只需检查您的请求模型属性状态是否为空。

最后,根据您的模型验证状态,您可以构建新创建的

ValidationResult
并更新用户。

另一种方法,通过使用 ModelBinderProvider

来做到这一点

但是,根据您的场景,我认为使用 httpcontext 访问器操作过滤器机制更合适。所以我提供这个例子。

让我们看看如何在实践中实现:

检查http请求上下文:

public class HttpContextAccessorActionFilter : IActionFilter
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public HttpContextAccessorActionFilter(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        context.HttpContext.Items["ActionName"] = context.ActionDescriptor.RouteValues["action"];
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do nothing
    }
}

注意: 在这里,我们使用 IHttpContextAccessor 检查每个 http 请求上下文的请求来源。

使用 IHttpContextAccessor 验证请求模型:

我们看下面的截图:

如您所见,我必须执行您所说的任何操作,即登录和注册。因此,在下面的模型验证器 acion 过滤器中,我们将检查两条路由以验证我们的

AuthUser request model

[AttributeUsage(AttributeTargets.Class)]
public class ConditionalValidationAttribute : ValidationAttribute
{
    public string[] Actions { get; }

    public ConditionalValidationAttribute(params string[] actions)
    {
        Actions = actions;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var model = value as AuthUser;

        if (model != null)
        {
            var httpContextAccessor = validationContext.GetRequiredService<IHttpContextAccessor>();
            var actionName = httpContextAccessor.HttpContext.Request.RouteValues["action"]?.ToString();
            var reqeustPath = httpContextAccessor.HttpContext.Request.Path;
            if (Actions.Contains(actionName))
            {
                // Validate based on the action
                if (actionName == "Login" && (string.IsNullOrEmpty(model.Email) || string.IsNullOrEmpty(model.Password)))
                {
                    return new ValidationResult("Email and Password are required for login.", new[] { nameof(AuthUser.Email), nameof(AuthUser.Password) });
                }
                else if (actionName == "Register" && (string.IsNullOrEmpty(model.Email) || string.IsNullOrEmpty(model.Password) || string.IsNullOrEmpty(model.FirstName) || string.IsNullOrEmpty(model.LastName)))
                {
                    return new ValidationResult("All fields are required for registration.", new[] { nameof(AuthUser.Email), nameof(AuthUser.Password), nameof(AuthUser.FirstName), nameof(AuthUser.LastName) });
                }
            }
        }

        return ValidationResult.Success;
    }
}

注意: 如您所见,我正在根据每个请求路由检查模型,并且它仅适用于

AuthUser

Auth用户模型:

您的模型应如下所示:

[ConditionalValidation("Login", "Register")]
public class AuthUser : IdentityUser
{
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;

    
}

程序.cs:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<HttpContextAccessorActionFilter>();
});

控制器:

[HttpPost]
public IActionResult Register(AuthUser model)
{
    return View("Register");
}

输出:

注意:如果您还需要使用ModelBinderProvider实现的方法,您可以参考这个答案或查看官方文档

© www.soinside.com 2019 - 2024. All rights reserved.