【问题标题】:Customising the OWIN/Katana UserManager factory behaviour自定义 OWIN/Katana UserManager 工厂行为
【发布时间】:2015-03-05 16:11:53
【问题描述】:

网上有很多示例使用 OWIN/Katana 根据用户名/密码组合在数据库中查找用户并生成声明主体,例如...

var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
// generate claims here...

如果您正在创建一个新应用程序并希望 Entity Framework 完成繁琐的工作,那很好。但是,我有一个 8 年历史的单体网站,它刚刚更新为使用基于声明的身份验证。我们的数据库命中是通过 DAL/SQL 手动完成的,然后从那里生成 ClaimsIdentity。

有些人建议 OWIN 比我们的手动方法更易于使用,但我希望使用它的人提供一些意见。

是否可以更改 UserManager 工厂根据用户凭据查找用户的方式?或者,有没有我错过的另一种方法?我可以在网上找到的所有示例似乎都使用了让 Entity Framework 创建数据库和管理搜索的样板方法。

【问题讨论】:

  • 是的,您可以自定义 UserManager 的行为方式。它获取数据的方式是通过IUserStore 的实现。这是一个答案,可以帮助您了解如何自定义它。 stackoverflow.com/questions/19940014/…

标签: asp.net entity-framework asp.net-identity owin katana


【解决方案1】:

我想说,ASP.NET 身份有点过于复杂。
2014 年 8 月,他们宣布了新版本 2.1,事情又发生了变化。
首先让我们摆脱@​​987654327@:

Uninstall-Package Microsoft.AspNet.Identity.EntityFramework

现在我们实现我们自己定义的User 实现接口IUser (Microsoft.AspNet.Identity):

public class User: IUser<int>
{
    public User()
    {
        this.Roles = new List<string>();
        this.Claims = new List<UserClaim>();
    }

    public User(string userName)
        : this()
    {
        this.UserName = userName;
    }

    public User(int id, string userName): this()
    {
        this.Id = Id;
        this.UserName = userName;
    }

    public int Id { get; set; }
    public string UserName { get; set; }
    public string PasswordHash { get; set; }

    public bool LockoutEnabled { get; set; }
    public DateTime? LockoutEndDateUtc { get; set; }
    public bool TwoFactorEnabled { get; set; }

    public IList<string> Roles { get; private set; }
    public IList<UserClaim> Claims { get; private set; }
}

如您所见,我已经定义了 Id (int) 的类型。

然后你必须定义你的自定义 UserManager 继承自 Microsoft.AspNet.Identity.UserManager 指定你的用户类型和密钥类型。

public class UserManager : UserManager<User, int>
{
    public UserManager(IUserStore<User, int> store): base(store)
    {
        this.UserLockoutEnabledByDefault = false;
        // this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(10);
        // this.MaxFailedAccessAttemptsBeforeLockout = 10;
        this.UserValidator = new UserValidator<User, int>(this)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = false
        };

        // Configure validation logic for passwords
        this.PasswordValidator = new PasswordValidator
        {
            RequiredLength = 4,
            RequireNonLetterOrDigit = false,
            RequireDigit = false,
            RequireLowercase = false,
            RequireUppercase = false,
        };
    }
}

我已经在此处实施了我的验证规则,但如果您愿意,可以将其保留在外面。

UserManager 需要 UserStore (IUserStore)。

您将在此处定义您的数据库逻辑。有几个接口需要实现。但并非所有这些都是强制性的。

public class UserStore : 
    IUserStore<User, int>, 
    IUserPasswordStore<User, int>, 
    IUserLockoutStore<User, int>, 
    IUserTwoFactorStore<User, int>,
    IUserRoleStore<User, int>,
    IUserClaimStore<User, int>
{

    // You can inject connection string or db session
    public UserStore()
    {
    }

}

我没有包含每个接口的所有方法。完成后,您就可以编写新用户了:

public System.Threading.Tasks.Task CreateAsync(User user)
{
}

按 ID 获取:

public System.Threading.Tasks.Task<User> FindByIdAsync(int userId)
{
}

等等。

然后你需要定义你的SignInManager继承自Microsoft.AspNet.Identity.Owin.SignInManager

public class SignInManager: SignInManager<User, int>
{
    public SignInManager(UserManager userManager, IAuthenticationManager authenticationManager): base(userManager, authenticationManager)
    {
    }

    public override Task SignInAsync(User user, bool isPersistent, bool rememberBrowser)
    {
        return base.SignInAsync(user, isPersistent, rememberBrowser);
    }
}

我只实现了SignInAsync:它会生成一个ClaimsIdentity

差不多了。

现在在Startup 类中,您必须告诉Owin 如何创建UserManagerSignInManager

app.CreatePerOwinContext<Custom.Identity.UserManager>(() => new Custom.Identity.UserManager(new Custom.Identity.UserStore()));
// app.CreatePerOwinContext<Custom.Identity.RoleManager>(() => new Custom.Identity.RoleManager(new Custom.Identity.RoleStore()));
app.CreatePerOwinContext<Custom.Identity.SignInService>((options, context) => new Custom.Identity.SignInService(context.GetUserManager<Custom.Identity.UserManager>(), context.Authentication));

我没有使用你会在默认模板中找到的工厂,因为我想让事情尽可能简单

并使您的应用程序能够使用 cookie:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider
        {
         // Enables the application to validate the security stamp when the user logs in.
         // This is a security feature which is used when you change a password or add an external login to your account.  
         OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<Custom.Identity.UserManager, Custom.Identity.User, int>(
         validateInterval: TimeSpan.FromMinutes(30),
         regenerateIdentityCallback: (manager, user) =>
         {
        var userIdentity = manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
                return (userIdentity);
    },
        getUserIdCallback: (id) => (Int32.Parse(id.GetUserId()))
        )}
}); 

现在在您的帐户控制器(或负责登录的控制器)中,您必须获得 UserManagerSignInManager

public Custom.Identity.SignInManager SignInManager
{
    get
    {
    return HttpContext.GetOwinContext().Get<Custom.Identity.SignInManager>();
    }
}

public Custom.Identity.UserManager UserManager
{
    get
    {
    return HttpContext.GetOwinContext().GetUserManager<Custom.Identity.UserManager>();
    }
}

您将使用SignInManager 进行登录:

var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

UserManager 来创建用户、添加角色和声明:

if (ModelState.IsValid)
{
        var user = new Custom.Identity.User() { UserName = model.Email };

        var result = await UserManager.CreateAsync(user, model.Password);
        if (result.Succeeded)
    {
        // await UserManager.AddToRoleAsync(user.Id, "Administrators");
                // await UserManager.AddClaimAsync(user.Id, new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Country, "England"));

                await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);

        return RedirectToAction("Index", "Home");
    }
        AddErrors(result);
}

这似乎很复杂......而且它是......有点。

如果您想了解更多关于它的信息,herehere 有一个很好的解释。

如果你想运行一些代码并看看它是如何工作的,我已经整理了一些 codeBiggy 一起使用(因为我不想浪费太多时间来定义表格和类似的东西) .

如果您有机会从 github 存储库下载我的代码,您会注意到我创建了一个辅助项目 (Custom.Identity),其中保存了我的所有 ASP.NET 身份 东西。

您需要的唯一 nuget 包是:

  1. Microsoft.AspNet.Identity.Core
  2. Microsoft.AspNet.Identity.Owin

【讨论】:

  • 同意你的看法。我想知道我们是否真的需要通过所有这些步骤来实现一些应该很简单的东西。驱动程序将拥有自己的表/模式以确保安全。我 - 就个人而言 - 不喜欢实体框架并想摆脱它。也许是可扩展性。在某些时候,您可能会决定切换到一些 NoSql 数据库。
  • 同意实体框架。出于兴趣,您是如何对上述代码如此熟悉的?是否有人为您指明了正确的方向,并且您从经验中吸取了教训?我发现我没有足够的时间来跟上 ASP.NET 的快速变化......
  • 我已经使用 Owin 和 web.api 已经有一段时间了。我正在开发一个项目,它是一个混合移动应用程序,带有由 web.api 提供的不记名令牌。我喜欢这个概念,所以我想在我目前正在处理的一个新的 MVC5 项目中复制这个逻辑。由于我使用 nHibernate,我想摆脱 EF 并将其替换为我自己的 nHibernate 实现。另一个原因是默认的 MVC5 模板不再使用表单身份验证。我浏览了大量博客和论文,并研究了示例解决方案。你可以在 github 上找到那个。
  • 是的,我完全同意。目前我什至正在考虑放弃所有这些东西并回到我的常规身份验证/授权管道。众所周知,Microsoft 会每两年改变一次,甚至更糟的是放弃一次。
  • 它会改变的。这是肯定的。我的看法是他们在玩它。看看所有这些接口。您真的需要这种抽象级别吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-09
  • 2017-05-13
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多