简体   繁体   English

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

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

I need to create a Web API C# application for an existing MySQL database. 我需要为现有的MySQL数据库创建一个Web API C#应用程序。 I've managed to use Entity Framework 6 to bind every database table to a RESTful API (that allows CRUD operations) . 我已经设法使用Entity Framework 6将每个数据库表绑定到RESTful API (允许CRUD操作)

I want to implement a login/registration system (so that I can implement roles and permissions in the future, and restrict certain API requests) . 我想实现一个登录/注册系统(这样我将来可以实现角色和权限,并限制某些API请求)

The MySQL database I have to use has a table for users (called user ) that has the following self-explanatory columns: 我必须使用的MySQL数据库有一个用户表(称为user ,其中包含以下不言自明的列:

  • id
  • email
  • username
  • password_hash

It seems that the de-facto standard for authentication is ASP.Net Identity. 似乎事实上的身份验证标准是ASP.Net Identity。 I have spent the last hour trying to figure out how to make Identity work with an existing DB-First Entity Framework setup. 我花了最后一小时试图弄清楚如何使Identity与现有的DB-First Entity Framework设置一起工作。

If I try to construct ApplicationUser instances storing user instances (entities from the MySQL database) to retrieve user data, I get the following error: 如果我尝试构建存储user实例(来自MySQL数据库的实体)的 ApplicationUser实例来检索用户数据,我会收到以下错误:

The entity type ApplicationUser is not part of the model for the current context. 实体类型ApplicationUser不是当前上下文的模型的一部分。

I assume I need to store Identity data in my MySQL database, but couldn't find any resource on how to do that. 我假设我需要在我的MySQL数据库中存储身份数据,但找不到任何有关如何执行此操作的资源。 I've tried completely removing the ApplicationUser class and making my user entity class derive from IdentityUser , but calling UserManager.CreateAsync resulted in LINQ to Entities conversion errors. 我已经尝试完全删除ApplicationUser类并使我的user实体类派生自IdentityUser ,但调用UserManager.CreateAsync导致LINQ to Entities转换错误。

How do I setup authentication in a Web API 2 application, having an existing user entity? 如何在具有现有user实体的Web API 2应用程序中设置身份验证?

You say: 你说:

I want to implement a login/registration system (so that I can implement roles and permissions in the future, and restrict certain API requests). 我想实现一个登录/注册系统(这样我将来可以实现角色和权限,并限制某些API请求)。

How do I setup authentication in a Web API 2 application, having an existing user entity? 如何在具有现有用户实体的Web API 2应用程序中设置身份验证?

It definitely means that you DO NOT need ASP.NET Identity. 这无疑意味着你不需要 ASP.NET身份。 ASP.NET Identity is a technology to handle all users stuffs. ASP.NET Identity是一种处理所有用户资料的技术。 It actually does not "make" the authentication mechanism. 它实际上并没有“制造”认证机制。 ASP.NET Identity uses OWIN Authentication mechanism, which is another thing. ASP.NET Identity使用OWIN身份验证机制,这是另一回事。

What you are looking for is not "how to use ASP.NET Identity with my existing Users table" , but "How to configure OWIN Authentication using my existing Users table" 您正在寻找的不是“如何将ASP.NET Identity与我现有的Users表一起使用” ,而是“如何使用我现有的Users表配置OWIN身份验证”

To use OWIN Auth follow these steps: 要使用OWIN Auth,请执行以下步骤:

Install the packages: 安装包:

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

Create Startup.cs file inside the root folder (example): 在根文件夹中创建Startup.cs文件(示例):

make sure that [assembly: OwinStartup] is correctly configured 确保正确配置[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");
            }
        }
    }
}

Use the [Authorize] attribute to authorize the actions. 使用[Authorize]属性授权操作。

Call api/security/token with GrantType , UserName , and Password to get the bearer token. 使用GrantTypeUserNamePassword调用api/security/token以获取承载令牌。 Like this: 像这样:

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

Send the token within the HttpHeader Authorization as Bearer "YOURTOKENHERE" . HttpHeader Authorization中将令牌作为Bearer "YOURTOKENHERE" Like this: 像这样:

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

Hope it helps! 希望能帮助到你!

Since your DB schema are not compatible with default UserStore You must implement your own UserStore and UserPasswordStore classes then inject them to UserManager . 由于您的数据库架构与默认的UserStore不兼容您必须实现自己的UserStoreUserPasswordStore类,然后将它们注入UserManager Consider this simple example: 考虑这个简单的例子:

First write your custom user class and implement IUser interface: 首先编写自定义用户类并实现IUser接口:

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

Now author your custom UserStore and IUserPasswordStore class like this: 现在创建您的自定义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);
    }
}

Now you have very own user store simply inject it to the user manager: 现在您拥有自己的用户存储,只需将其注入用户管理器:

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
    }
}

Also please note you must directly inherit your DB Context class from DbContext not IdentityDbContext since you have implemented own user store. 另请注意,您必须直接从DbContext继承您的DB Context类, DbContext不是IdentityDbContext因为您已经实现了自己的用户存储。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM