ASP.NET核心MVC身份 - 添加临时(会话)索赔



对于初学者,此处也提出了此问题(在ASP.NET核心身份中基于临时会话的索赔(和此处(如何动态地向用户添加索赔?(,但都没有收到任何问题回答,所以我试图恢复它...

我正在创建一个多租户网络应用程序。用户登录,他们可以看到与自己的公司有关的数据(但没有使用该应用程序的其他公司的数据(。由于具有多个店面等的特许经营团体,单个用户需要访问多个公司并不少见,但是在这种情况下,他们在登录时必须选择一家公司。

几乎所有数据查询都需要公司ID作为参数,因此我需要一种方便的方法来检查当前登录用户的哪个公司。如果他们登录并登录其他公司,我需要查看另一家公司ID。我想将其存储为可以在会话期间看到的身份声称,但我不一定要将其存储到数据库中,因为它可能会随着每个登录而更改。

这是我的登录操作(基于标准ASP.NET Core MVC模板(:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;
    if (ModelState.IsValid)
    {
        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            // BEGIN CUSTOM CODE - Check which companies a user can access
            IEnumerable<int> allCompanies = _myDbService.GetUserCompanies(model.Email);
            if (allCompanies.Count() == 1)
            {
                // then they can only access one company - log them into it automatically.
                // I need easy access to its ID with the User since it is used with almost every db query.
                int companyID = allCompanies[0];
                ((ClaimsIdentity)User.Identity).AddClaim(new Claim("CompanyID", companyID.ToString())); // this shows as null on future controller actions.
                Debug.WriteLine("Set breakpoint here to examine User"); // I see 1 claim (my custom claim) in the debugger, but it is not in the database yet.
                // future controller actions show me 3 claims (nameidentifier, name, and security stamp) but User.Claims.First(c => c.Type == "CompanyID").Value throws error.
                return RedirectToLocal(returnUrl);
            }
            else
            {
                // the user has access to several companies - make them choose one to complete login process
                RedirectToAction("ChooseCompany", new { companyList = allCompanies });
            }
            // END CUSTOM CODE
        }
        // REMAINING CODE OMITTED FOR BREVITY
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}

所以我的问题是:为什么自定义主张"粘贴",以便在未来的控制器动作中看到它?如果这是标准行为,为什么不保存到数据库?是否可以在不使用AddSession((的情况下在会话期间保持此价值?如果我以索赔的形式实施此功能,我是否每次访问该值时都会往返数据库?

对于其他任何人都有这个问题,这是我到目前为止...要永久存储索赔并在登录处自动加载索赔,您必须使用以下和更改直到下一个登录时间才生效:

await userManager.AddClaimAsync(user, new Claim("your-claim", "your-value"));

使用以下内容仅存储当前会话的索赔,因此我在正确的轨道上:

((ClaimsIdentity)User.Identity).AddClaim(new Claim("your-claim", "your-value"));

我发现在控制器操作之间获得"粘贴"索赔的唯一方法是覆盖UserClaimSprinCipalFactory,以便ASP.NET将您的自定义代码作为登录过程的一部分。这里的问题在于,您只能以相关方法访问Applicationuser,并且您无法真正传递任何自定义参数AFAIK。因此,我首先通过Applicationuser类进行了修改:

public class ApplicationUser : IdentityUser
{
    public long ActiveDealershipID { get; set; }
} 

(您必须应用EF迁移(。现在,我创建了一个从userclaimsprincipalfactory派生的类:

public class MyClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
    public MyClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager, IOptions<IdentityOptions> options) : base(userManager, roleManager, options)
    {
    }
    public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
    {
        var principal = await base.CreateAsync(user);
        long dealershipID = user.ActiveDealershipID;
        ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("DealershipID", dealershipID.ToString()));
        return principal;
    }
}

(您必须在startup.cs configureservices中注册服务(:

services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, MyClaimsPrincipalFactory>();

然后在我的登录操作中,我在登录之前将新用户属性设置为新用户属性,以便在设置索赔时可以向UserClaimSprinCipalFactory使用:

public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;
    if (ModelState.IsValid)
    {
        ApplicationUser user = await _userManager.FindByEmailAsync(model.Email);
        user.ActiveDealershipID = model.ChosenDealership
        await _userManager.UpdateAsync(user);
        var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
        if (result.Succeeded)
        {
            _logger.LogInformation("User logged in.");
            ...

我仍然对更好的建议或其他想法开放,但这似乎对我有用。

相关内容

  • 没有找到相关文章