CreateIdentityAsync多次调用FindByIdAsync

时间:2014-06-08 22:48:26

标签: c# asp.net asp.net-mvc-5 asp.net-identity asp.net-identity-2

我使用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次(!!!)。这是非常不理想的。实际上它甚至应该调用它,因为已经检索了用户对象。

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;
    }
}