【问题标题】:DB-First authentication confusion with ASP.NET Web API 2 + EF6DB-First 身份验证与 ASP.NET Web API 2 + EF6 混淆
【发布时间】:2016-01-28 16:38:29
【问题描述】:

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

我想实现一个登录/注册系统(这样我以后可以实现角色和权限,并限制某些 API 请求)

我必须使用的 MySQL 数据库有一个用户表(称为user,其中包含以下不言自明的列:

  • id
  • email
  • username
  • password_hash

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

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

实体类型 ApplicationUser 不是当前上下文模型的一部分。

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

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

【问题讨论】:

    标签: c# asp.net entity-framework asp.net-web-api2 ef-database-first


    【解决方案1】:

    你说:

    我想实现一个登录/注册系统(这样我就可以 未来实现角色和权限,并限制某些 API 请求)。

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

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

    您要查找的不是“如何将 ASP.NET Identity 与我现有的 Users 表一起使用”,而是 “如何使用我现有的 Users 表配置 OWIN 身份验证”强>

    要使用 OWIN 身份验证,请按以下步骤操作:

    安装包:

    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 }
    

    希望对你有帮助!

    【讨论】:

    • 谢谢,这就是我要找的。抱歉,如果我的问题不清楚,但我对 ASP.NET Identity 的“角色”感到困惑。
    • 不客气,伙计。如果您在实施过程中遇到问题,请随时在此处发表评论
    • 嘿,你能告诉我上面代码中的 userService 对象是什么吗?只是一个自定义类对象,它将连接到我的数据库上下文并从数据库返回我的自定义用户?用户不必在 IUser 或任何东西之后继承?
    • 是的,它是一个从数据库中检索用户的自定义类。是的,用户不必继承任何东西,因为在上面的例子中,没有使用身份。
    • 非常感谢!我终于设法让身份验证工作了!问题还在于,许多事情是自动发生的,并不像在程序集中自动搜索称为类的 OWIN Startup 那样明显。
    【解决方案2】:

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

    首先编写你的自定义用户类并实现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 类,如下所示:

    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 继承您的 DB Context 类,而不是 IdentityDbContext,因为您已经实现了自己的用户存储。

    【讨论】:

      猜你喜欢
      • 2022-08-13
      • 2012-06-16
      • 1970-01-01
      • 2014-01-28
      • 2017-10-22
      • 2023-04-08
      • 1970-01-01
      • 2015-09-17
      • 2016-07-17
      相关资源
      最近更新 更多