CreateIdentityAsync调用FindByIdAsync多次



我正在使用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;
    }
}

相关内容

  • 没有找到相关文章

最新更新