我正在开发一个使用 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 在一段时间后丢失自定义声明信息