我需要为现有的MySQL数据库创建一个Web API C#应用程序。我设法使用Entity Framework 6将每个数据库表绑定到RESTful API (允许CRUD操作)。
我想实现一个登录/注册系统(以便我以后可以实现角色和权限,并限制某些API请求)。
我必须使用的MySQL数据库有一个用户(称为user
)的表,其中包含以下不言自明的列:
id
email
username
password_hash
似乎事实上的身份验证标准是ASP.Net Identity。我花了最后一小时试图弄清楚如何使用现有的DB-First Entity Framework设置来使用Identity。
如果我尝试构建ApplicationUser
实例存储user
实例(来自MySQL数据库的实体)来检索用户数据,我会收到以下错误:
实体类型ApplicationUser不是当前上下文模型的一部分。
我假设我需要在我的MySQL数据库中存储身份数据,但找不到任何有关如何执行此操作的资源。我已尝试完全删除ApplicationUser
类并使user
实体类派生自IdentityUser
,但调用UserManager.CreateAsync
会导致LINQ to Entities转换错误。
如何在具有现有user
实体的Web API 2应用程序中设置身份验证?
答案 0 :(得分:21)
你说:
我想实现一个登录/注册系统(这样我就可以 在将来实施角色和权限,并限制某些 API请求)。
如何在Web API 2应用程序中设置身份验证,具有 现有的用户实体?
这绝对意味着您不要需要ASP.NET身份。 ASP.NET Identity是一种处理所有用户资料的技术。它实际上并没有" make"认证机制。 ASP.NET Identity使用OWIN身份验证机制,这是另一回事。
您正在寻找的不是"如何将ASP.NET身份用于我现有的用户表" ,但"如何配置OWIN身份验证使用我现有的用户表"
要使用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.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]
属性授权操作。
使用api/security/token
,GrantType
和UserName
致电Password
以获取持有人令牌。像这样:
"grant_type=password&username=" + username + "&password=" password;
将HttpHeader Authorization
内的令牌发送为Bearer "YOURTOKENHERE"
。像这样:
headers: { 'Authorization': 'Bearer ' + token }
希望它有所帮助!
答案 1 :(得分:7)
Since your DB schema are not compatible with default UserStore
You must implement your own UserStore
and UserPasswordStore
classes then inject them to UserManager
. Consider this simple example:
First write your custom user class and implement IUser
interface:
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:
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.