为什么在登录操作中添加声明后,无法在其他控制器中访问它



我正在开发一个使用 ASP.NET MVC 5 和 Identity 2 和实体框架 6 的系统。当用户登录时,我会向该登录会话添加一些声明。我不想使用索赔表。

对于我的一个主张,我确实是这样的:

public class User : IdentityUser<int, UserLogin, UserRole, UserClaim>
{
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User, int> manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
        //We add the display name so that the _LoginPartial can pick it up;
        userIdentity.AddClaim(new Claim("DisplayName", FirstName + " " + LastName));
        // Add custom user claims here
        return userIdentity;
    }
    public virtual ICollection<UserInsurance> UserInsurances { get; set; }
    public User()
    {
        UserInsurances = new List<UserInsurance>();
    }
}

对于访问声明:

var claimsIdentity = User.Identity as System.Security.Claims.ClaimsIdentity;
var displayNameClaim = claimsIdentity != null
    ? claimsIdentity.Claims.SingleOrDefault(x => x.Type == "DisplayName")
    : null;
var nameToDisplay = displayNameClaim == null ? User.Identity.Name : displayNameClaim.Value;

这很好用。但问题是当我需要一个不在用户表中的字段时。事实上,它是用户导航属性(UserInsurances)中的一条记录,我需要一个linq查询来访问它。

var lastUserInsurance = UserInsurances.OrderByDescending(x => x.CompanyInsuranceId).First();
userIdentity.AddClaim(new Claim("CompanyInsuranceId", lastUserInsurance.CompanyInsuranceId.ToString()));

如果我把这段代码放在GenerateUserIdentityAsync方法中,比如"DisplayName",UserInsurances就是空的。因此,我应该将此代码添加到登录操作中,并在用户成功登录后添加。但我试过了,它不起作用。我不知道为什么,但是当我想访问该声明时,它不存在。

public virtual async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }
        var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                var user = _user.Include(x => x.UserInsurances).FirstOrDefault(x => x.NationalCode == model.UserName);
                var identity = await SignInManager.CreateUserIdentityAsync(user);
                var lastUserInsurance = user.UserInsurances.OrderByDescending(x => x.CompanyInsuranceId).FirstOrDefault();
                identity.AddClaim(new Claim("CompanyInsuranceId", lastUserInsurance.CompanyInsuranceId.ToString()));
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                return View(model);
        }
    }

谁能告诉我为什么我无法访问此声明并且它不存在?我不知道如何实现此方案并在应用程序的所有部分中访问"公司保险 ID"声明。

必须先添加声明,然后才能让用户登录。因此,如果出于任何原因您无法GenerateUserIdentityAsync方法填写索赔。只需在登录操作方法中生成Identity对象,然后登录即可。请考虑以下示例:

public async Task<ActionResult> Login(LoginViewModel model,string returnUrl)
{
    var user = UserManager.Find(model.Email, model.Password);
    // now you have the user object do what you to gather claims
    if(user!=null)
    {
        var ident = UserManager.CreateIdentity(user, 
            DefaultAuthenticationTypes.ApplicationCookie);
            ident.AddClaims(new[] {
                new Claim("MyClaimName","MyClaimValue"),
                new Claim("YetAnotherClaim","YetAnotherValue"),
        });
        AuthenticationManager.SignIn(
            new AuthenticationProperties() { IsPersistent = true }, 
            ident);
        return RedirectToLocal(returnUrl);
    }
    ModelState.AddModelError("", "Invalid login attempt.");
    return View(model);
} 

如您所见,您可以执行任何想要收集声明并填写标识然后登录用户的操作。

但是,如果要使用SignInManager.PasswordSignInAsync()方法,只需以某种方式覆盖SignInManager.CreateUserIdentityAsync()方法,以便生成所需的声明。例如,如果您需要DbContext获取额外的信息来提供您的索赔,您只需在SignInManager中注入DbContext并在CreateUserIdentityAsync()方法中使用它,如下所示:

public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
    private readonly ApplicationDbContext _context;
    public ApplicationSignInManager(
        ApplicationUserManager userManager, 
        IAuthenticationManager authenticationManager,
        ApplicationDbContext context)
        : base(userManager, authenticationManager)
    {
        _context=context;
    }
    public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
    {
        var companyInsuranceId=_context.Users
            .Where(u=>u.NationalCode == user.UserName)
            .Select(u=>u.UserInsurances
                .OrderByDescending(x => x.CompanyInsuranceId)
                .Select(x=>x.CompanyInsuranceId)
                .FirstOrDefault())
            .FirstOrDefault();
        var ident=user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
        ident.AddClaim(new Claim("CompanyInsuranceId",
            companyInsuranceId.ToString()));
        return ident;
    }
    public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
    {
        return new ApplicationSignInManager(
            context.GetUserManager<ApplicationUserManager>(),
            context.Authentication,
            context.Get<ApplicationDbContext>());
    }
}

现在只需通过写作

var result = await SignInManager.PasswordSignInAsync(
    model.UserName, model.Password, 
    model.RememberMe, shouldLockout: false);

您可以让用户登录并注入其他声明。

对于 MVC5,可以通过 ApplicationUser 类轻松添加其他声明。

例如。

    public ClaimsIdentity GenerateUserIdentity(ApplicationUserManager manager)
    {
        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = manager.CreateIdentity(this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }

这也解决了身份 Cookie 在一段时间后丢失自定义声明信息

相关内容

  • 没有找到相关文章

最新更新