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

配置身份验证:

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 次(!!!)。这是愚蠢的次优。事实上,它甚至应该调用它,因为已经检索到用户对象。

4

3 回答 3

1

我的解决方案确实是“特定于实现的”(因为我为 MongoDB 实现了自己的身份存储,所以我将缓存设置在这个级别,这让我可以比通用解决方案更好地控制),但如果这对任何人都有帮助,我将我的源代码发布到http://pastebin.com/MV0F4MUA

ConfigureAuth然后,您通过设置这是您的方法来“调用”每个请求的缓存:

app.CreatePerOwinContext<BaseApplicationUserManager>((_options,_context) => BaseApplicationUserManager.Create(new AuthenticationProviderRequestCache<BaseApplicationUser>(authenticationProvider),_options,_context));

警告:您不能简单地将我的代码复制/粘贴到您的解决方案中,您需要了解它以使其适应您的需求。

于 2014-08-22T16:25:18.847 回答
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()来源

于 2015-10-27T14:53:58.290 回答
0

我只需要为一个较旧的网络应用程序解决这个问题,所以这是我为仍在搜索的任何人提供的解决方案。查看存档的 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;
    }
}
于 2019-04-06T02:26:51.413 回答