IdentityServer4注册UserService并从asp.net核心中的数据库获取用户

我已经search了如何在asp.net核心注册一个UserService与IdentityServer4,但我似乎不能find正确的方法来做到这一点。

这是注册InMemoryUsers在这里find的代码,但是我想访问我的MSSQL数据库中的用户不是样本中定义的静态用户。

 var builder = services.AddIdentityServer(options => { options.SigningCertificate = cert; }); builder.AddInMemoryClients(Clients.Get()); builder.AddInMemoryScopes(Scopes.Get()); builder.AddInMemoryUsers(Users.Get()); 

那么我看看这是IdentityServer3

 var factory = new IdentityServerServiceFactory() .UseInMemoryClients(Clients.Get()) .UseInMemoryScopes(Scopes.Get()); var userService = new UserService(); factory.UserService = new Registration<IUserService>(resolver => userService); 

从网上看,似乎我需要使用DI系统注册UserService,但我不知道它如何绑定到身份服务器,例如。

 services.AddScoped<IUserService, UserService>(); 

所以我的问题是:

如何将我的UserService绑定到构build器(IdentityServer4用户)? 我将如何去调用我的数据库访问和身份validationUserService (我使用存储库连接到数据库)我现有的数据库用户?

考虑到这一点asp.net核心工作。

谢谢!

在IdentityServer4中。 IUserService不再可用,现在您必须使用IResourceOwnerPasswordValidator进行身份validation并使用“IProfileService”来获取声明。

在我的情况下,我使用资源所有者授予types,而我所需要的只是让用户根据用户名和密码为我的Web API执行基于angular色的授权。 而且我认为这个主题对于每个用户都是独一无二的。

我已经在下面发布了我的代码,它可以正常工作。 谁能告诉我,有关于我的代码的任何问题?

在startup.cs中注册这两个服务。

 public void ConfigureServices(IServiceCollection services) { var builder = services.AddIdentityServer(); builder.AddInMemoryClients(Clients.Get()); builder.AddInMemoryScopes(Scopes.Get()); builder.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>(); builder.Services.AddTransient<IProfileService, ProfileService>(); } 

实现IResourceOwnerPasswordValidator接口。

 public class ResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator { public Task<customgrantvalidationresult> ValidateAsync(string userName, string password, ValidatedTokenRequest request) { // Check The UserName And Password In Database, Return The Subject If Correct, Return Null Otherwise // subject = ...... if (subject == null) { var result = new CustomGrantValidationResult("Username Or Password Incorrect"); return Task.FromResult(result); } else { var result = new CustomGrantValidationResult(subject, "password"); return Task.FromResult(result); } } } 

实现ProfileService接口。

 public class ProfileService : IProfileService { public Task GetProfileDataAsync(ProfileDataRequestContext context) { string subject = context.Subject.Claims.ToList().Find(s => s.Type == "sub").Value; try { // Get Claims From Database, And Use Subject To Find The Related Claims, As A Subject Is An Unique Identity Of User //List<string> claimStringList = ...... if (claimStringList == null) { return Task.FromResult(0); } else { List<Claim> claimList = new List<Claim>(); for (int i = 0; i < claimStringList.Count; i++) { claimList.Add(new Claim("role", claimStringList[i])); } context.IssuedClaims = claimList.Where(x => context.RequestedClaimTypes.Contains(x.Type)); return Task.FromResult(0); } } catch { return Task.FromResult(0); } } public Task IsActiveAsync(IsActiveContext context) { return Task.FromResult(0); } } 

更新 – IdentityServer 4已经更改并用IResourceOwnerPasswordValidatorIProfileServicereplace了IUserService

我用我的UserRepository从数据库中获取所有的用户数据。 这是注入(DI)到构造函数中,并在Startup.cs定义。 我还为身份服务器(也被注入)创build了以下类:

首先定义ResourceOwnerPasswordValidator.cs

 public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { //repository to get user from db private readonly IUserRepository _userRepository; public ResourceOwnerPasswordValidator(IUserRepository userRepository) { _userRepository = userRepository; //DI } //this is used to validate your user account with provided grant at /connect/token public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { try { //get your user model from db (by username - in my case its email) var user = await _userRepository.FindAsync(context.UserName); if (user != null) { //check if password match - remember to hash password if stored as hash in db if (user.Password == context.Password) { //set the result context.Result = new GrantValidationResult( subject: user.UserId.ToString(), authenticationMethod: "custom", claims: GetUserClaims(user)); return; } context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Incorrect password"); return; } context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "User does not exist."); return; } catch (Exception ex) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password"); } } //build claims array from user data public static Claim[] GetUserClaims(User user) { return new Claim[] { new Claim("user_id", user.UserId.ToString() ?? ""), new Claim(JwtClaimTypes.Name, (!string.IsNullOrEmpty(user.Firstname) && !string.IsNullOrEmpty(user.Lastname)) ? (user.Firstname + " " + user.Lastname) : ""), new Claim(JwtClaimTypes.GivenName, user.Firstname ?? ""), new Claim(JwtClaimTypes.FamilyName, user.Lastname ?? ""), new Claim(JwtClaimTypes.Email, user.Email ?? ""), new Claim("some_claim_you_want_to_see", user.Some_Data_From_User ?? ""), //roles new Claim(JwtClaimTypes.Role, user.Role) }; } 

ProfileService.cs

 public class ProfileService : IProfileService { //services private readonly IUserRepository _userRepository; public ProfileService(IUserRepository userRepository) { _userRepository = userRepository; } //Get user profile date in terms of claims when calling /connect/userinfo public async Task GetProfileDataAsync(ProfileDataRequestContext context) { try { //depending on the scope accessing the user data. if (!string.IsNullOrEmpty(context.Subject.Identity.Name)) { //get user from db (in my case this is by email) var user = await _userRepository.FindAsync(context.Subject.Identity.Name); if (user != null) { var claims = GetUserClaims(user); //set issued claims to return context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList(); } } else { //get subject from context (this was set ResourceOwnerPasswordValidator.ValidateAsync), //where and subject was set to my user id. var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub"); if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0) { //get user from db (find user by user id) var user = await _userRepository.FindAsync(long.Parse(userId.Value)); // issue the claims for the user if (user != null) { var claims = ResourceOwnerPasswordValidator.GetUserClaims(user); context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList(); } } } } catch (Exception ex) { //log your error } } //check if user account is active. public async Task IsActiveAsync(IsActiveContext context) { try { //get subject from context (set in ResourceOwnerPasswordValidator.ValidateAsync), var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "user_id"); if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0) { var user = await _userRepository.FindAsync(long.Parse(userId.Value)); if (user != null) { if (user.IsActive) { context.IsActive = user.IsActive; } } } } catch (Exception ex) { //handle error logging } } } 

然后在Startup.cs我做了以下操作:

 public void ConfigureServices(IServiceCollection services) { //... //identity server 4 cert var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "idsrv4test.pfx"), "your_cert_password"); //DI DBContext inject connection string services.AddScoped(_ => new YourDbContext(Configuration.GetConnectionString("DefaultConnection"))); //my user repository services.AddScoped<IUserRepository, UserRepository>(); //add identity server 4 services.AddIdentityServer() .AddSigningCredential(cert) .AddInMemoryIdentityResources(Config.GetIdentityResources()) //check below .AddInMemoryApiResources(Config.GetApiResources()) .AddInMemoryClients(Config.GetClients()) .AddProfileService<ProfileService>(); //Inject the classes we just created services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>(); services.AddTransient<IProfileService, ProfileService>(); //... } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { //... app.UseIdentityServer(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); IdentityServerAuthenticationOptions identityServerValidationOptions = new IdentityServerAuthenticationOptions { //move host url into appsettings.json Authority = "http://localhost:50000/", ApiSecret = "secret", ApiName = "my.api.resource", AutomaticAuthenticate = true, SupportedTokens = SupportedTokens.Both, // required if you want to return a 403 and not a 401 for forbidden responses AutomaticChallenge = true, //change this to true for SLL RequireHttpsMetadata = false }; app.UseIdentityServerAuthentication(identityServerValidationOptions); //... } 

您还需要Config.cs来定义您的客户,api和资源。 你可以在这里find一个例子: https : //github.com/IdentityServer/IdentityServer4.Demo/blob/master/src/IdentityServer4Demo/Config.cs

您现在应该可以调用IdentityServer / connect / token

在这里输入图像说明

有关更多信息,请查看文档: https : //media.readthedocs.org/pdf/identityserver4/release/identityserver4.pdf


旧的答案 (这不适用于更新的IdentityServer4)

一旦你了解事物的stream动,它就非常简单了。

像这样configuration您的IdentityService(在Startup.cs – ConfigureServices() ):

 var builder = services.AddIdentityServer(options => { options.SigningCertificate = cert; }); builder.AddInMemoryClients(Clients.Get()); builder.AddInMemoryScopes(Scopes.Get()); //** this piece of code DI's the UserService into IdentityServer ** builder.Services.AddTransient<IUserService, UserService>(); //for clarity of the next piece of code services.AddTransient<IUserRepository, UserRepository>(); 

然后设置你的UserService

 public class UserService : IUserService { //DI the repository from Startup.cs - see previous code block private IUserRepository _userRepository; public UserService(IUserRepository userRepository) { _userRepository = userRepository; } public Task AuthenticateLocalAsync(LocalAuthenticationContext context) { var user = _userRepository.Find(context.UserName); //check if passwords match against user column //My password was hashed, //so I had to hash it with the saved salt first and then compare. if (user.Password == context.Password) { context.AuthenticateResult = new AuthenticateResult( user.UserId.ToString(), user.Email, //I set up some claims new Claim[] { //Firstname and Surname are DB columns mapped to User object (from table [User]) new Claim(Constants.ClaimTypes.Name, user.Firstname + " " + user.Surname), new Claim(Constants.ClaimTypes.Email, user.Email), new Claim(Constants.ClaimTypes.Role, user.Role.ToString()), //custom claim new Claim("company", user.Company) } ); } return Task.FromResult(0); } public Task GetProfileDataAsync(ProfileDataRequestContext context) { //find method in my repository to check my user email var user = _userRepository.Find(context.Subject.Identity.Name); if (user != null) { var claims = new Claim[] { new Claim(Constants.ClaimTypes.Name, user.Firstname + " " + user.Surname), new Claim(Constants.ClaimTypes.Email, user.Email), new Claim(Constants.ClaimTypes.Role, user.Role.ToString(), ClaimValueTypes.Integer), new Claim("company", user.Company) }; context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)); } return Task.FromResult(0); } public Task IsActiveAsync(IsActiveContext context) { var user = _userRepository.Find(context.Subject.Identity.Name); return Task.FromResult(user != null); } } 

基本上通过将UserService注入到( IdentityServerBuildertypes) Services builder中,允许它在auth上调用UserService。

我希望这可以帮助别人,因为花了我几个小时才能完成这个任务。

在IdentityServer4 1.0.0-rc5中,IUserService和CustomGrantValidationResult都不可用。

现在,而不是返回一个CustomGrantValidationResult你将需要设置context.Result。

  public class ResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator { private MyUserManager _myUserManager { get; set; } public ResourceOwnerPasswordValidator() { _myUserManager = new MyUserManager(); } public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { var user = await _myUserManager.FindByNameAsync(context.UserName); if (user != null && await _myUserManager.CheckPasswordAsync(user,context.Password)) { context.Result = new GrantValidationResult( subject: "2", authenticationMethod: "custom", claims: someClaimsList); } else { context.Result = new GrantValidationResult( TokenRequestErrors.InvalidGrant, "invalid custom credential"); } return; } 

资源所有者密码validation