在 ASP.NET Core Web 应用程序中使用身份登录期间向用户添加声明

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

编辑2:

终于想通了。查看下面已接受的答案。

编辑:

按照@Jason Pan 的指示,我尝试了完整的源代码。 https://github.com/affableashish/blazor-server-auth/tree/feature/AddClaimsDuringLogin

在登录期间添加了声明(在 Login.cshtml.cs 文件中)并从 Razor 组件访问这些声明。

不幸的是,没有成功。我只得到

null
作为索赔值。 😔


原始问题

我见过一些类似的问题,例如thisthis,但它们对我的场景没有帮助。

我的应用程序是一个 Blazor Server 项目,我按照此处提到的步骤添加了 Identity

现在这就是我想要实现的目标:

  1. 用户输入他们的凭据。
  2. 如果用户名有效(在我们的 Active Directory 中),我会从 Active Directory 中检索一个名为
    EmployeeId
    的字段。
  3. 使用
    SignInManager.PasswordSignInAsync
    对用户进行身份验证。
  4. EmployeeId
    添加为 ClaimsPrincipal 的声明。 (这样我就可以像这样使用 Razor Components 中的
    EmployeeId
    )。
  5. 我正在努力弄清楚如何在登录过程中
添加此

EmployeeId

作为声明
我的

OnPostAsync

中的

Login.cshtml.cs
方法如下所示:
public class LoginModel : PageModel
{
    private readonly SignInManager<MMTUser> _signInManager;
    private readonly ILogger<LoginModel> _logger;

    public LoginModel(SignInManager<MMTUser> signInManager, ILogger<LoginModel> logger)
    {
        _signInManager = signInManager;
        _logger = logger;
    }

    [BindProperty]
    public InputModel Input { get; set; }

    public string ReturnUrl { get; set; }

    [TempData]
    public string ErrorMessage { get; set; }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl ??= Url.Content("~/");

        if (ModelState.IsValid)
        {
            // Step 1: Check if this user exists in our AD
            // If YES: Grab the Employee Id and go to next step
            // If NO: Terminate the process
            var adLookupResult = ADHelper.ADLookup(Input.Username);
            if (adLookupResult == null || string.IsNullOrEmpty(adLookupResult.EmployeeId))
            {
                ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                return Page();
            }

            // Step 2: SignIn the user
            var result = await _signInManager.PasswordSignInAsync(Input.Username, Input.Password, isPersistent: Input.RememberMe, lockoutOnFailure: false);

            // Step 3: How do I add adLookupResult.EmployeeId to the ClaimsPrincipal?

            if (result.Succeeded)
            {
                _logger.LogInformation("User logged in.");
                return LocalRedirect(returnUrl);
            }
            else
            {
                ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                return Page();
            }
        }

        // If we got this far, something failed, redisplay form
        return Page();
    }
    
    public class InputModel
    {
        [Required]
        public string Username { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
    }
}


c# asp.net-core asp.net-identity .net-6.0 blazor-server-side
2个回答
2
投票
aspnetcore

github 存储库中提出问题解决了这个问题。 https://github.com/dotnet/aspnetcore/issues/46558 非常感谢@Piotr Stola 和@Jason Pan 的帮助!

第1步:

OnPostAsync

中的

Login.cshtml.cs
方法中,使用
_signInManager.SignInWithClaimsAsync
代替
_signInManager.PasswordSignInAsync
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
    returnUrl ??= Url.Content("~/");

    if (ModelState.IsValid)
    {
        // Step 1: Check if this user exists in our AD
        // If YES: Grab the Employee Id and go to next step
        // If NO: Terminate the process
        var adLookupResult = // Call Active Directory and get EmployeeId of this user here
        if (adLookupResult == null || string.IsNullOrEmpty(adLookupResult.EmployeeId))
        {
            ModelState.AddModelError(string.Empty, "User doesn't exist.");
            return Page();
        }

        // Step 2: Check if the user exists in our Identity database (AspNetUsers table)
        var user = await _signInManager.UserManager.FindByNameAsync(Input.Email);
        if (user == null)
        {
            ModelState.AddModelError(string.Empty, "User doesn't exist.");
            return Page();
        }
        
        // Step 3: Check the credentials of this user.
        var result = await _signInManager.CheckPasswordSignInAsync(user, Input.Password, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            // Access 'EmployeeId' from Areas/Identity/Components/TakeABreak.razor
            // For eg: Emp123 is adLookupResult.EmployeeId that I retrieved from Active Directory in Step 1.
            var customClaims = new[] { new Claim("EmployeeId", "Emp123") };
            await _signInManager.SignInWithClaimsAsync(user, Input.RememberMe, customClaims);
            _logger.LogInformation("User logged in.");
            return LocalRedirect(returnUrl);
        }
        else
        {
            ModelState.AddModelError(string.Empty, "Invalid login attempt.");
            return Page();
        }
    }

    // If we got this far, something failed, redisplay form
    return Page();
}

第2步:

从任何 Razor 组件访问它。例如:我从

TakeABreak.razor

组件访问它(附图中的第 15 行)。


完整源代码

https://github.com/akhanalcs/blazor-server-auth/tree/feature/AddClaimsDuringLogin


0
投票

如果放入中间件,之前的解决方案可能是一种解决方法。那太可怕了。

我们应该使用 CustomClaimsPrincipalFactory 来解决这个问题。

CustomClaimsPrincipalFactory.cs

using HMT.Web.Server.Areas.Identity; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using System.Security.Claims; namespace HMT.Web.Server { public class CustomClaimsPrincipalFactory : UserClaimsPrincipalFactory<HMTUser> { public CustomClaimsPrincipalFactory( UserManager<HMTUser> userManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor) { } // This method is called only when login. It means that "the drawback // of calling the database with each HTTP request" never happen. public async override Task<ClaimsPrincipal> CreateAsync(HMTUser user) { var principal = await base.CreateAsync(user); if (principal.Identity != null) { //((ClaimsIdentity)principal.Identity).AddClaims( // new[] { new Claim("EmpId", user.EmpId) }); ((ClaimsIdentity)principal.Identity).AddClaims( new[] { new Claim("EmpId", "EmpId123") }); } return principal; } } }

Program.cs 或 Startup.cs

// Learned the hard way that this needs to be added after setting up Blazor (i.e. AddRazorPages, AddServerSideBlazor) - AshishK builder.Services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<HMTUser>>(); builder.Services.AddScoped<IUserClaimsPrincipalFactory<HMTUser>, CustomClaimsPrincipalFactory>();

欲了解更多详情,您可以查看
链接

成功登录后,可以使用
HttpContext.User.AddIdentity

来实现。示例如下:

   // Step 3: Add the EmployeeId to ClaimsPrincipal
    if (result.Succeeded)
    {
        HttpContext.User.AddIdentity(new ClaimsIdentity(new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, adLookupResult.EmployeeId)
        }));
        _logger.LogInformation("User logged in.");
        return LocalRedirect(returnUrl);
    }

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