我使用ASP.NET Identity 2.0和我自己的自定义商店。 我注意到多次调用存储操作,特别是在登录时。
这是我的登录代码(几乎包含在默认模板中的内容):
[AllowAnonymous]
public async Task<ActionResult> LogIn(LogInModel model)
{
if(model!=null && (!string.IsNullOrEmpty(model.Email) || !string.IsNullOrEmpty(model.Password)))
{
model.DisplayValidationMessages=true;
if(ModelState.IsValid)
{
BaseApplicationUser user=await UserManager.FindAsync(model.Email,model.Password);
if(user!=null)
{
await SignInAsync(user,model.RememberMe);
return Redirect((model.ContinueUrl??"/")+"#"+model.State.UrlEncode());
}
model.ErrorMessage="Those credentials are invalid, please try again";
}
}
return View(model);
}
protected async Task SignInAsync(BaseApplicationUser user,bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
AuthenticationManager.SignIn(
new AuthenticationProperties { IsPersistent=isPersistent },
await user.GenerateUserIdentityAsync(UserManager)
);
}
我的用户扩展如下:
public class BaseApplicationUser:User
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<BaseApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
ClaimsIdentity userIdentity=await manager.CreateIdentityAsync(this,DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
ConfigureAuth:
public void ConfigureAuth(IAppBuilder app)
{
[...]
// Configure the db context and user manager to use a single instance per request
//app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<BaseApplicationUserManager>((_options,_context) => BaseApplicationUserManager.Create(usersStore,_options,_context));
app.CreatePerOwinContext<BaseApplicationRoleManager>((_options,_context) => BaseApplicationRoleManager.Create(rolesStore,_options,_context));
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions {
AuthenticationType=DefaultAuthenticationTypes.ApplicationCookie,
LoginPath=new PathString("/Authentication/LogIn"),
CookieSecure=CookieSecureOption.Always,
CookieHttpOnly=true,
Provider=new CookieAuthenticationProvider {
OnValidateIdentity=SecurityStampValidator.OnValidateIdentity<BaseApplicationUserManager,BaseApplicationUser>(
TimeSpan.FromMinutes(30),
(_manager,_user) => _user.GenerateUserIdentityAsync(_manager)
)
}
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
[...]
}
问题:
登录后,使用BaseApplicationUser user=await UserManager.FindAsync(model.Email,model.Password);
检索用户,这绝对正常。
当调用ClaimsIdentity userIdentity=await manager.CreateIdentityAsync(this,DefaultAuthenticationTypes.ApplicationCookie);
时,会传递一个BaseApplicationUser,因此不需要像在用户商店那样在用户商店中调用FindByIdAsync
3次(!!!)。这是非常不理想的。实际上它甚至应该调用它,因为已经检索了用户对象。
答案 0 :(得分:1)
我的解决方案确实是&#34;具体实施&#34; (因为我为MongoDB实现了我自己的身份存储,我将缓存设置在这个级别,这使我能够比通用解决方案更好地控制),但如果这对任何人都有帮助,我将我的源代码发布到{{3 }}
然后你打电话&#34;通过设置此请求的缓存是您的ConfigureAuth
方法:
app.CreatePerOwinContext<BaseApplicationUserManager>((_options,_context) => BaseApplicationUserManager.Create(new AuthenticationProviderRequestCache<BaseApplicationUser>(authenticationProvider),_options,_context));
警告:您不能简单地将我的代码复制/粘贴到您的解决方案中,您需要了解它以使其适应您的需求。
答案 1 :(得分:0)
我遇到同样的问题,经过一些搜索(不会太长,因为没有人对发生的事情发表了正确的解释)我最终使用原来的方法编写了我自己的CreateIdentityAsync
方法版本Microsoft.AspNet.Identity.Core
命名空间作为参考,这里是:
public ClaimsIdentity CreateAsync(IdentityUser user, string authenticationType)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
ClaimsIdentity claimsIdentity = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie, ClaimTypes.NameIdentifier, ClaimTypes.Role);
claimsIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString(), "http://www.w3.org/2001/XMLSchema#string"));
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, user.UserName, "http://www.w3.org/2001/XMLSchema#string"));
claimsIdentity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "ASP.NET Identity", "http://www.w3.org/2001/XMLSchema#string"));
return claimsIdentity;
}
但是这仍然是一种解决方法,直到我能理解UserStore.FindByIdAsync()
的来电来自哪里
答案 2 :(得分:0)
我只需要为一个较旧的Web应用程序解决此问题,因此这是仍在搜索的任何人的解决方案。查看存档的AspNet Identity源(https://github.com/aspnet/AspNetIdentity)。对IUserStore.FindByIdAsync()
进行三个调用的原因是由于Microsoft.AspNet.Identity.ClaimsIdentityFactory
在添加声明时将用户ID传递给了三个方法,这又调用了IUserStore.FindByIdAsync()
。问题是,在我的情况下,调用方法已经拥有了我需要的用户对象,因此我的解决方案是重写这些方法以接受用户对象并照常进行。
public class UserManager : Microsoft.AspNet.Identity.UserManager<Employee, int>
{
public UserManager(IUserStore<Employee, int> store) : base(store)
{
ClaimsIdentityFactory = new ClaimsIdentityFactory();
}
...
public override async Task<ClaimsIdentity> CreateIdentityAsync(Employee user, string authenticationType)
{
if (user != null && /* user is active, etc */)
{
var userIdentity = await ClaimsIdentityFactory.CreateAsync(this, user, authenticationType);
...
return userIdentity;
}
else
{
return null;
}
}
...
public async Task<string> GetSecurityStampAsync(Employee user)
{
var securityStore = Store as IUserSecurityStampStore<Employee, int>;
if (securityStore == null)
{
throw new NotSupportedException("User Store Not IUserSecurityStampStore");
}
return await securityStore.GetSecurityStampAsync(user).WithCurrentCulture();
}
public async Task<IList<string>> GetRolesAsync(Employee user)
{
var userRoleStore = Store as IUserRoleStore<Employee, int>;
if (userRoleStore == null)
{
throw new NotSupportedException("User Store Not IUserRoleStore");
}
return await userRoleStore.GetRolesAsync(user).WithCurrentCulture();
}
public virtual async Task<IList<Claim>> GetClaimsAsync(Employee user)
{
var claimStore = Store as IUserClaimStore<Employee, int>;
if (claimStore == null)
{
throw new NotSupportedException("User Store Not IUserClaimStore");
}
return await claimStore.GetClaimsAsync(user).WithCurrentCulture();
}
}
public class ClaimsIdentityFactory : Microsoft.AspNet.Identity.ClaimsIdentityFactory<Employee, int>
{
...
public override async Task<ClaimsIdentity> CreateAsync(Microsoft.AspNet.Identity.UserManager<Employee, int> manager, Employee user, string authenticationType)
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
var id = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType);
id.AddClaim(new Claim(UserIdClaimType, ConvertIdToString(user.Id), ClaimValueTypes.String));
id.AddClaim(new Claim(UserNameClaimType, user.UserName, ClaimValueTypes.String));
id.AddClaim(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String));
if (manager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(SecurityStampClaimType, await (manager as UserManager).GetSecurityStampAsync(user).WithCurrentCulture()));
}
if (manager.SupportsUserRole)
{
IList<string> roles = await (manager as UserManager).GetRolesAsync(user).WithCurrentCulture();
foreach (string roleName in roles)
{
id.AddClaim(new Claim(RoleClaimType, roleName, ClaimValueTypes.String));
}
}
if (manager.SupportsUserClaim)
{
id.AddClaims(await (manager as UserManager).GetClaimsAsync(user).WithCurrentCulture());
}
return id;
}
}