ASP.NET 5标识 - 自定义SignInManager

Dil*_*rov 27 c# asp.net-identity-2 asp.net-core-mvc asp.net-core

我有一个MVC 6项目(vNext),我正在玩ASP.NET身份.在我的情况下,我不想使用使用EF(SignInManager,UserManager,UserStore)的内置东西.我有一个外部数据库,我只想进行用户名/密码查找并返回一个有效的cookie.所以我开始写自己的课程.

public class MyUser
{
    public string Id { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string PasswordHash { get; set; }
}

public class MyUserStore : IUserStore<MyUser>, IUserPasswordStore<MyUser>
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

在MyUserStore课堂上我使用硬编码的用户列表作为我的商店(仅用于测试目的).我重写了一些方法只是为了从硬编码的商店中返回数据.

public class MyUserManager : UserManager<MyUser>
{
    public MyUserManager(
        IUserStore<MyUser> store,
        IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<MyUser> passwordHasher,
        IEnumerable<IUserValidator<MyUser>> userValidators,
        IEnumerable<IPasswordValidator<MyUser>> passwordValidators,
        ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors,
        IEnumerable<IUserTokenProvider<MyUser>> tokenProviders,
        ILoggerFactory logger,
        IHttpContextAccessor contextAccessor) :
        base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, tokenProviders, logger, contextAccessor)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里,我制作了方法CheckPasswordAsync并VerifyPasswordAsync返回true并PasswordVerificationResult.Success分别仅用于测试.

public class MyClaimsPrincipleFactory : IUserClaimsPrincipalFactory<MyUser>
{
    public Task<ClaimsPrincipal> CreateAsync(MyUser user)
    {
        return Task.Factory.StartNew(() =>
        {
            var identity = new ClaimsIdentity();
            identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
            var principle = new ClaimsPrincipal(identity);

            return principle;
        });
    }
}

public class MySignInManager : SignInManager<MyUser>
{
    public MySignInManager(MyUserManager userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<MyUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor = null, ILoggerFactory logger = null)
            : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger)
    {
    }

    public override Task<SignInResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
    {
        // here goes the external username and password look up

        if (userName.ToLower() == "username" && password.ToLower() == "password")
        {
            return base.PasswordSignInAsync(userName, password, isPersistent, shouldLockout);
        }
        else
        {
            return Task.FromResult(SignInResult.Failed);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所有内容都在Startup课堂上如下:

services.AddIdentity<MyUser, MyRole>()
            .AddUserStore<MyUserStore>()
            .AddUserManager<MyUserManager>()
            .AddDefaultTokenProviders();
Run Code Online (Sandbox Code Playgroud)

而且因为我没有设法MySignInManager在Startup代码中创建对象以便将其添加到DI中(以便稍后在控制器和视图中注入),所以我在它中创建它MyAccountController.

public MyAccountController(IHttpContextAccessor httpContextAccessor, UserManager<MyUser> userManager, IOptions<IdentityOptions> optionsAccessor, ILoggerFactory logger)
{
    SignInManager = new MySignInManager(userManager as MyUserManager, httpContextAccessor, new MyClaimsPrincipleFactory(), optionsAccessor, logger);
}
Run Code Online (Sandbox Code Playgroud)

在我MyLogin操作的MyAccount控制器中我正在调用PasswordSignInAsync,我可以看到我正在获取带有编码声明的cookie(来自MyClaimsPrincipleFactory).当我尝试使用AuthorizeAttributeon 来调用其他操作时,我可以看到cookie位于请求标头中但我未经授权(更准确地说,因为我没有从视觉中删除内置的默认ASP.NET身份验证工作室示例模板,我被重定向到帐户/登录).

这是自定义ASP.NET身份的正确方法吗?我在这里缺少什么?

Mis*_*hie 10

我也有尝试使用自定义的问题SignInManager,毕竟实现起来真的很容易.

在Startup.cs,默认执行之后services.Identity

services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();
Run Code Online (Sandbox Code Playgroud)

您只需要注入以下内置DI:

services.AddScoped<SignInManager<MyApplicationUser>, MySignInManager>();
Run Code Online (Sandbox Code Playgroud)

默认值SignInManager由自定义覆盖.


Joe*_*tte 6

在我的项目中,我有一个不使用EF的身份的工作实现.我想也许你的实施超出了你的需要.UserManager和SignInManager与EF无关.如果你愿意,你可以实现这些,但你不必只是为了摆脱EF.如果您需要验证硬编码密码以进行测试,您只需要实现UserStore和RoleStore以及PasswordHasher.

services.TryAdd(ServiceDescriptor.Scoped<IUserStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserPasswordStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserEmailStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserLoginStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserRoleStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserClaimStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserPhoneNumberStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserLockoutStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserTwoFactorStore<SiteUser>, UserStore<SiteUser>>());
services.TryAdd(ServiceDescriptor.Scoped<IRoleStore<SiteRole>, RoleStore<SiteRole>>());
services.TryAdd(ServiceDescriptor.Scoped<IUserClaimsPrincipalFactory<SiteUser>, SiteUserClaimsPrincipalFactory<SiteUser, SiteRole>>());
services.TryAdd(ServiceDescriptor.Transient<IPasswordHasher<SiteUser>, SitePasswordHasher<SiteUser>>());
services.AddIdentity<SiteUser, SiteRole>();
Run Code Online (Sandbox Code Playgroud)

上面显示了我为了绕过Entity Framework而实现的项目,我的AccountController例如采用了构造函数参数

    UserManager<SiteUser> userManager,
            SignInManager<SiteUser> signInManager
Run Code Online (Sandbox Code Playgroud)

这是标准的身份UserManager和SignInManager,我没有设置DI服务,他们为我注册了这条线:

services.AddIdentity<SiteUser, SiteRole>();
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到该扩展方法的代码.它是身份的一部分,不属于EFIdentity.你可以看到我也实现了IUserClaimsPrincipalFactory.我实现的唯一原因是添加一些自定义声明,我不需要这样做就可以远离EF.

  • 你在Startup.cs中仍然有这一行吗?app.UseIdentity(); 这是设置cookie身份验证的那个 (2认同)

Cal*_*alC 5

您可以SignInManager使用方法IdentityBuilder.AddSignInManager中的方法注册依赖注入的自定义Startup.ConfigureServices,如下所示:

services.AddIdentity<MyUser, IdentityRole<int>>()
    .AddUserStore<UserStore<MyUser, IdentityRole<int>, SqlContext, int>>()
    .AddRoleStore<RoleStore<IdentityRole<int>, SqlContext, int>>()
    .AddSignInManager<SignInManager<MyUser>>()
    .AddDefaultTokenProviders();
Run Code Online (Sandbox Code Playgroud)

您所实现的 没有理由SignInManager不能以相同的方式注册 DI。


Dil*_*rov 4

这里的问题是我没有提供AuthenticationType我的ClaimsIdentity对象。这篇博文对我有帮助。

\n
\n

要将 IsAuthenticated 设置为 true,您需要在构造函数中指定身份验证类型:

\n

var id = new ClaimsIdentity(claims, \xe2\x80\x9cCustom\xe2\x80\x9d);

\n
\n