终于想通了。查看下面已接受的答案。
按照@Jason Pan 的指示,我尝试了完整的源代码。 https://github.com/affableashish/blazor-server-auth/tree/feature/AddClaimsDuringLogin
在登录期间添加了声明(在 Login.cshtml.cs 文件中)并从 Razor 组件访问这些声明。
不幸的是,没有成功。我只得到
null
作为索赔值。 😔
我见过一些类似的问题,例如this和this,但它们对我的场景没有帮助。
我的应用程序是一个 Blazor Server 项目,我按照此处提到的步骤添加了 Identity。
现在这就是我想要实现的目标:
EmployeeId
的字段。SignInManager.PasswordSignInAsync
对用户进行身份验证。EmployeeId
添加为 ClaimsPrincipal 的声明。 (这样我就可以像这样使用 Razor Components 中的 EmployeeId
)。
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; }
}
}
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步:
TakeABreak.razor
组件访问它(附图中的第 15 行)。
完整源代码 https://github.com/akhanalcs/blazor-server-auth/tree/feature/AddClaimsDuringLogin我们应该使用 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;
}
}
}
// 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);
}