DB-First 身份验证与 ASP.NET Web API 2 + EF6 混淆

DB-First authentication confusion with ASP.NET Web API 2 + EF6

我需要为现有 MySQL 数据库创建 Web API C# 应用程序。我设法使用 Entity Framework 6 将每个数据库 table 绑定到 RESTful API (允许 CRUD 操作)

我想实现一个login/registration系统(这样我可以在未来实现角色和权限,并限制某些API请求)

我必须使用的 MySQL 数据库有一个 table 用户 (称为 user 具有以下不言自明的列:

似乎认证的事实标准是ASP.Net身份。我花了最后一个小时试图弄清楚如何使身份与现有的 DB-First Entity Framework 设置一起工作。

如果我尝试构造 ApplicationUser 个实例存储 user 个实例 (来自 MySQL 数据库的实体) 来检索用户数据,我出现以下错误:

The entity type ApplicationUser is not part of the model for the current context.

我假设我需要将身份数据存储在我的 MySQL 数据库中,但找不到任何关于如何做到这一点的资源。我已经尝试完全删除 ApplicationUser class 并使我的 user 实体 class 派生自 IdentityUser,但是调用 UserManager.CreateAsync 导致 LINQ to实体转换错误。

如何在具有现有 user 实体的 Web API 2 应用程序中设置身份验证?

由于您的数据库架构与默认 UserStore 不兼容,您必须实现自己的 UserStoreUserPasswordStore class 然后将它们注入 UserManager .考虑这个简单的例子:

首先编写自定义用户class并实现IUser接口:

class User:IUser<int>
{
    public int ID {get;set;}
    public string Username{get;set;}
    public string Password_hash {get;set;}
    // some other properties 
}

现在像这样编写您的自定义 UserStoreIUserPasswordStore class:

public class MyUserStore : IUserStore<User>, IUserPasswordStore<User>
{
    private readonly MyDbContext _context;

    public MyUserStore(MyDbContext context)
    {
        _context=context;
    }

    public Task CreateAsync(AppUser user)
    {
        // implement your desired logic such as
        // _context.Users.Add(user);
    }

    public Task DeleteAsync(AppUser user)
    {
        // implement your desired logic
    }

    public Task<AppUser> FindByIdAsync(string userId)
    {
        // implement your desired logic
    }

    public Task<AppUser> FindByNameAsync(string userName)
    {
        // implement your desired logic
    }

    public Task UpdateAsync(AppUser user)
    {
        // implement your desired logic
    }

    public void Dispose()
    {
        // implement your desired logic
    }

    // Following 3 methods are needed for IUserPasswordStore
    public Task<string> GetPasswordHashAsync(AppUser user)
    {
        // something like this:
        return Task.FromResult(user.Password_hash);
    }

    public Task<bool> HasPasswordAsync(AppUser user)
    {
        return Task.FromResult(user.Password_hash != null);
    }

    public Task SetPasswordHashAsync(AppUser user, string passwordHash)
    {
        user.Password_hash = passwordHash;
        return Task.FromResult(0);
    }
}

现在您拥有自己的用户存储,只需将其注入用户管理器即可:

public class ApplicationUserManager: UserManager<User, int>
{
    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
    {
         var manager = new ApplicationUserManager(new MyUserStore(context.Get<MyDbContext>()));
         // rest of code
    }
}

另请注意,您必须直接从 DbContext 而不是 IdentityDbContext 继承您的数据库上下文 class,因为您已经实现了自己的用户存储。

你说:

I want to implement a login/registration system (so that I can implement roles and permissions in the future, and restrict certain API requests).

How do I setup authentication in a Web API 2 application, having an existing user entity?

这绝对意味着您不需要需要ASP.NET身份。 ASP.NET Identity 是一种处理所有用户事务的技术。它实际上没有"make" 身份验证机制。 ASP.NET Identity使用OWIN Authentication机制,这是另外一回事

你要找的不是"how to use ASP.NET Identity with my existing Users table",而是"How to configure OWIN Authentication using my existing Users table"

要使用 OWIN Auth,请按照以下步骤操作:

安装软件包:

Owin
Microsoft.AspNet.Cors
Microsoft.AspNet.WebApi.Client
Microsoft.AspNet.WebApi.Core
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.WebApi.WebHost
Microsoft.Owin
Microsoft.Owin.Cors
Microsoft.Owin.Host.SystemWeb
Microsoft.Owin.Security
Microsoft.Owin.Security.OAuth

在根文件夹中创建 Startup.cs 文件(示例):

确保 [assembly: OwinStartup] 已正确配置

[assembly: OwinStartup(typeof(YourProject.Startup))]
namespace YourProject
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var config = new HttpConfiguration();
            //other configurations

            ConfigureOAuth(app);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }

        public void ConfigureOAuth(IAppBuilder app)
        {
            var oAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/api/security/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
                Provider = new AuthorizationServerProvider()
            };

            app.UseOAuthAuthorizationServer(oAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        }
    }

    public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            context.Validated();
        }

        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {
            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

            try
            {
                //retrieve your user from database. ex:
                var user = await userService.Authenticate(context.UserName, context.Password);

                var identity = new ClaimsIdentity(context.Options.AuthenticationType);

                identity.AddClaim(new Claim(ClaimTypes.Name, user.Name));
                identity.AddClaim(new Claim(ClaimTypes.Email, user.Email));

                //roles example
                var rolesTechnicalNamesUser = new List<string>();

                if (user.Roles != null)
                {
                    rolesTechnicalNamesUser = user.Roles.Select(x => x.TechnicalName).ToList();

                    foreach (var role in user.Roles)
                        identity.AddClaim(new Claim(ClaimTypes.Role, role.TechnicalName));
                }

                var principal = new GenericPrincipal(identity, rolesTechnicalNamesUser.ToArray());

                Thread.CurrentPrincipal = principal;

                context.Validated(identity);
            }
            catch (Exception ex)
            {
                context.SetError("invalid_grant", "message");
            }
        }
    }
}

使用 [Authorize] 属性授权操作。

使用 GrantTypeUserNamePassword 调用 api/security/token 以获取不记名令牌。像这样:

"grant_type=password&username=" + username + "&password=" password;

HttpHeader Authorization 内的令牌发送为 Bearer "YOURTOKENHERE"。像这样:

headers: { 'Authorization': 'Bearer ' + token }

希望对您有所帮助!