我正在使用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次(!!)。这是非常不理想的。事实上,它甚至应该调用这个,因为用户对象已经被检索到。
我的解决方案真的是"具体实现"(因为我实现了我自己的身份存储MongoDB,我将缓存设置在这个级别,这允许我更好地控制比通用的解决方案),但如果这可以对任何人有任何帮助,我发布了我的源代码http://pastebin.com/MV0F4MUA
然后通过设置以下ConfigureAuth
方法为每个请求"调用"缓存:
app.CreatePerOwinContext<BaseApplicationUserManager>((_options,_context) => BaseApplicationUserManager.Create(new AuthenticationProviderRequestCache<BaseApplicationUser>(authenticationProvider),_options,_context));
警告:你不能简单地复制/粘贴我的代码到你的解决方案,你需要了解它,使其适应你的需要。
我也有同样的问题,经过一些搜索(不太长,因为没有人发布过一个适当的解释正在发生的事情),我最终使用Microsoft.AspNet.Identity.Core
命名空间中实现的原始方法编写了我自己的CreateIdentityAsync
方法版本作为参考,这里是:
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()
的调用来自
我只是要解决这个旧的web应用程序,所以这里是我的解决方案,任何人仍在搜索。查看已归档的asp.net 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;
}
}