为ASP.NET 5编写自定义身份验证类

时间:2016-01-26 10:49:49

标签: c# asp.net asp.net-identity

开箱即用的ASP.NET 5 Identity类是直接的,它易于设置和使用,但我的问题是我使用的遗留系统与我拥有的现有用户和权限表自定义身份系统。 Identity系统似乎非常可插拔,但我找不到任何关于如何编写自定义Identity类的正确文档。

是否有任何文件
  1. 如何编写可以满足旧系统用户和权限表的自定义ASP.NET 5标识类
  2. 如何设置自定义Identity类的使用以在ASP.NET 5,MVC 6应用程序中使用?

1 个答案:

答案 0 :(得分:3)

目前还没有很多可用的文档,所以我使用了最新的Identity类,目前是Microsoft.AspNet.Identity.EntityFramework 3.0.0-rc1-final,并提出了一个与我的旧用户数据库表一起使用的解决方案。

首先确保您的旧用户实体类实现IdentityUser类,以便我们可以在ASP.NET 5中使用该类进行身份验证

public class MyLegacyUser : IdentityUser
{
    // Your MyLegacyUser properties will go here as usual
}

确保忽略从您不想使用的IdentityUser类继承的任何属性(这些属性不包含在用户表中)。我们通过在OnModelCreating类的DbContext方法中使用流畅的api来实现此目的。

public class MyDbContext : DbContext
{
    public DbSet<MyLegacyUser> MyLegacyUser { get; set; }

    // For simplicity I will add only the OnModelCreating method here
    protected override void OnModelCreating
    {
        modelBuilder.Entity<MyLegacyUser>(entity =>
        {
            entity.Ignore(e => e.AccessFailedCount);
            entity.Ignore(e => e.Claims);
            entity.Ignore(e => e.ConcurrencyStamp);
            entity.Ignore(e => e.Email);
            entity.Ignore(e => e.EmailConfirmed);
            entity.Ignore(e => e.Id);
            entity.Ignore(e => e.LockoutEnabled);
            entity.Ignore(e => e.LockoutEnd);
            entity.Ignore(e => e.Logins);
            entity.Ignore(e => e.NormalizedEmail);
            entity.Ignore(e => e.NormalizedUserName);
            entity.Ignore(e => e.PasswordHash);
            entity.Ignore(e => e.PhoneNumber);
            entity.Ignore(e => e.PhoneNumberConfirmed);
            entity.Ignore(e => e.Roles);
            entity.Ignore(e => e.SecurityStamp);
            entity.Ignore(e => e.TwoFactorEnabled);
        }
    }
}

现在我们必须实现自己的自定义UserManager类,以便对我们的旧用户进行身份验证。确保您的新班级实施UserManager<T>,其中T是您的MyLegacyUser。完成此操作后,覆盖CheckPasswordAsync以对您的用户进行身份验证。

注意: CheckPasswordAsync方法不负责返回经过身份验证的用户,它只是一个返回true或false的方法,以指示用户是否已成功通过身份验证。经过身份验证的用户由另一个类设置,我将在下面解释。

public class MyLegacyUserManager : UserManager<MyLegacyUser>
{
    public WorldUserManager(IUserStore<MasterUser> store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<MasterUser> passwordHasher, IEnumerable<IUserValidator<MasterUser>> userValidators, IEnumerable<IPasswordValidator<MasterUser>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<MasterUser>> logger, IHttpContextAccessor contextAccessor) : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger, contextAccessor)
    {
    }

    public override async Task<bool> CheckPasswordAsync(MasterUser user, string password)
    {
        // This is my own authentication manager class that handles user authentication
       // Add your own code to authenticate your user here
        return new AuthenticationManager().Authenticate(user.EmailAddress, password);
    }
}    

完成此操作后,我们必须实现自己的UserStore类。您可以实现一些接口,例如IUserStore<T>IUserLoginStore<T>IUserClaimsStore<T>等。我实现了IUserClaimsStore<T>接口并实现了GetUserIdAsync,{{1 },GetUserNameAsyncFindByIdAsync方法

GetClaimsAsync

这些是自定义身份验证所需的所有类。不允许在public class MyLegacyUserClaimStore : IUserClaimStore<MyLegacyUser> { // Here I simply returned the username of the user parameter I recieved as input public Task<string> GetUserIdAsync(MasterUser user, CancellationToken cancellationToken) { return Task.Run(() => user.UserName, cancellationToken); } } // Here I simply returned the username of the user parameter I recieved as input public Task<string> GetUserNameAsync(MasterUser user, CancellationToken cancellationToken) { return Task.Run(() => user.UserName, cancellationToken); } public Task<MasterUser> FindByIdAsync(string userId, CancellationToken cancellationToken) { // This is my manager class to read my user for the userId // Add your own code to read the user for the set Id here return Task.Run(() => new MyLegacyUserUserManager().ReadForEmailAddress(userId, 0, true, true), cancellationToken); } public Task<MasterUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken) { // This is my manager class to read my user for the normalizedUserName // Add your own code to read the user for the set normalizedUserName here return Task.Run(() => new MyLegacyUserManager().ReadForEmailAddress(normalizedUserName, 0, true, true), cancellationToken); } // If you want to make use of Claims make sure that you map them here // If you do not use claims, consider implementing one of the other IUserStore interfaces //such as the IUserLoginStore so that you do not have to implement the GetClaimsAsync method public async Task<IList<Claim>> GetClaimsAsync(MasterUser user, CancellationToken cancellationToken) { var claims = new List<Claim>(); foreach (var claim in user.Claims) { claims.Add(new Claim(claim.ClaimType, claim.ClaimValue)); } return claims; } 类中配置我们的自定义身份验证方法。将以下内容添加到Startup.cs方法

ConfigureServices

public void ConfigureServices(IServiceCollection services) { // Use the default role, IdentityRole as we are not implementing roles // Add our custom UserManager and UserStore classes services.AddIdentity<MyLegacyUser, IdentityRole>(config => { config.User.RequireUniqueEmail = true; config.Cookies.ApplicationCookie.AccessDeniedPath = new Microsoft.AspNet.Http.PathString("/Auth/Login"); config.Cookies.ApplicationCookie.LoginPath = new Microsoft.AspNet.Http.PathString("/Auth/Login"); config.Cookies.ApplicationCookie.LogoutPath = new Microsoft.AspNet.Http.PathString("/Auth/Login"); }) .AddUserManager<MyLegacyUserManager>() .AddUserStore<MyLegacyUserUserClaimStore>() .AddEntityFrameworkStores<MyDbContext>(); } 方法中,请确保您指定要使用身份功能进行身份验证

注意:使用语句的顺序很重要,如果您使用的是Mvc,请务必在Configure之前加入UseIdentity

UseMvc

现在我们已经配置了自定义身份验证类,我们可以使用默认的public async void Configure(IApplicationBuilder app) { app.UseIdentity(); // Your useMvc and other use statements will go here } 类进行身份验证。以下是我的SignInManager

的示例
AuthController

当您的用户通过身份验证后,您可以像访问MVC 5一样访问用户声明,例如

public class AuthController : Controller
{
    private SignInManager<MyLegacyUserUser> _signInManager;

    public AuthController(SignInManager<MasterUser> signInManager)
    {
        _signInManager = signInManager;
    }

    // For simplicity I will only add the Login action here
    [HttpPost]
    public async Task<IActionResult> Login(LoginViewModel loginViewModel)
    {
        var result = await _signInManager.PasswordSignInAsync(loginViewModel.Username, loginViewModel.Password, true, false);

        if (result == SignInResult.Success)
        {
            return RedirectToAction("Index", "SomeControllerToRedirectTo");
        }

        await _signInManager.SignOutAsync();

        return RedirectToAction("Login", "Auth");
    }
}