IdentityServer4 密码模式 接入现有用户数据表

  • A+

IdentityServer4 密码模式 接入现有用户数据表

首先定义ResourceOwnerPasswordValidator.cs

  1. public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator  
  2. {  
  3.     //repository to get user from db  
  4.     private readonly IUserRepository _userRepository;  
  5.   
  6.     public ResourceOwnerPasswordValidator(IUserRepository userRepository)  
  7.     {  
  8.         _userRepository = userRepository; //DI  
  9.     }  
  10.   
  11.     //this is used to validate your user account with provided grant at /connect/token  
  12.     public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)  
  13.     {  
  14.         try  
  15.         {  
  16.             //get your user model from db (by username - in my case its email)  
  17.             var user = await _userRepository.FindAsync(context.UserName);  
  18.             if (user != null)  
  19.             {  
  20.                 //check if password match - remember to hash password if stored as hash in db  
  21.                 if (user.Password == context.Password) {  
  22.                     //set the result  
  23.                     context.Result = new GrantValidationResult(  
  24.                         subject: user.UserId.ToString(),  
  25.                         authenticationMethod: "custom",   
  26.                         claims: GetUserClaims(user));  
  27.   
  28.                     return;  
  29.                 }   
  30.   
  31.                 context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Incorrect password");  
  32.                 return;  
  33.             }  
  34.             context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "User does not exist.");  
  35.             return;  
  36.         }  
  37.         catch (Exception ex)  
  38.         {  
  39.             context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password");  
  40.         }  
  41.     }  
  42.   
  43.     //build claims array from user data  
  44.     public static Claim[] GetUserClaims(User user)  
  45.     {  
  46.         return new Claim[]  
  47.         {  
  48.             new Claim("user_id", user.UserId.ToString() ?? ""),  
  49.             new Claim(JwtClaimTypes.Name, (!string.IsNullOrEmpty(user.Firstname) && !string.IsNullOrEmpty(user.Lastname)) ? (user.Firstname + " " + user.Lastname) : ""),  
  50.             new Claim(JwtClaimTypes.GivenName, user.Firstname  ?? ""),  
  51.             new Claim(JwtClaimTypes.FamilyName, user.Lastname  ?? ""),  
  52.             new Claim(JwtClaimTypes.Email, user.Email  ?? ""),  
  53.             new Claim("some_claim_you_want_to_see", user.Some_Data_From_User ?? ""),  
  54.   
  55.             //roles  
  56.             new Claim(JwtClaimTypes.Role, user.Role)  
  57.         };  
  58. }  

然后定义 ProfileService.cs

  1. public class ProfileService : IProfileService  
  2. {  
  3.     //services  
  4.     private readonly IUserRepository _userRepository;  
  5.   
  6.     public ProfileService(IUserRepository userRepository)  
  7.     {  
  8.         _userRepository = userRepository;  
  9.     }  
  10.   
  11.     //Get user profile date in terms of claims when calling /connect/userinfo  
  12.     public async Task GetProfileDataAsync(ProfileDataRequestContext context)  
  13.     {  
  14.         try  
  15.         {  
  16.             //depending on the scope accessing the user data.  
  17.             if (!string.IsNullOrEmpty(context.Subject.Identity.Name))  
  18.             {  
  19.                 //get user from db (in my case this is by email)  
  20.                 var user = await _userRepository.FindAsync(context.Subject.Identity.Name);  
  21.   
  22.                 if (user != null)  
  23.                 {  
  24.                     var claims = GetUserClaims(user);  
  25.   
  26.                     //set issued claims to return  
  27.                     context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();  
  28.                 }  
  29.             }  
  30.             else  
  31.             {  
  32.                 //get subject from context (this was set ResourceOwnerPasswordValidator.ValidateAsync),  
  33.                 //where and subject was set to my user id.  
  34.                 var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "sub");  
  35.   
  36.                 if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)  
  37.                 {  
  38.                     //get user from db (find user by user id)  
  39.                     var user = await _userRepository.FindAsync(long.Parse(userId.Value));  
  40.   
  41.                     // issue the claims for the user  
  42.                     if (user != null)  
  43.                     {  
  44.                         var claims = ResourceOwnerPasswordValidator.GetUserClaims(user);  
  45.   
  46.                         context.IssuedClaims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList();  
  47.                     }  
  48.                 }  
  49.             }  
  50.         }  
  51.         catch (Exception ex)  
  52.         {  
  53.             //log your error  
  54.         }  
  55.     }  
  56.   
  57.     //check if user account is active.  
  58.     public async Task IsActiveAsync(IsActiveContext context)  
  59.     {  
  60.         try  
  61.         {  
  62.             //get subject from context (set in ResourceOwnerPasswordValidator.ValidateAsync),  
  63.             var userId = context.Subject.Claims.FirstOrDefault(x => x.Type == "user_id");  
  64.   
  65.             if (!string.IsNullOrEmpty(userId?.Value) && long.Parse(userId.Value) > 0)  
  66.             {  
  67.                 var user = await _userRepository.FindAsync(long.Parse(userId.Value));  
  68.   
  69.                 if (user != null)  
  70.                 {  
  71.                     if (user.IsActive)  
  72.                     {  
  73.                         context.IsActive = user.IsActive;  
  74.                     }  
  75.                 }  
  76.             }  
  77.         }  
  78.         catch (Exception ex)  
  79.         {  
  80.             //handle error logging  
  81.         }  
  82.     }  
  83. }  

最后在Startup.cs作以下修改

  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     //...  
  4.   
  5.     //identity server 4 cert  
  6.     var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "idsrv4test.pfx"), "your_cert_password");  
  7.   
  8.     //DI DBContext inject connection string  
  9.     services.AddScoped(_ => new YourDbContext(Configuration.GetConnectionString("DefaultConnection")));  
  10.   
  11.     //my user repository  
  12.     services.AddScoped<IUserRepository, UserRepository>();  
  13.   
  14.     //add identity server 4  
  15.     services.AddIdentityServer()  
  16.         .AddSigningCredential(cert)  
  17.         .AddInMemoryIdentityResources(Config.GetIdentityResources()) //check below  
  18.         .AddInMemoryApiResources(Config.GetApiResources())  
  19.         .AddInMemoryClients(Config.GetClients())  
  20.         .AddProfileService<ProfileService>();  
  21.   
  22.     //Inject the classes we just created  
  23.     services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();  
  24.     services.AddTransient<IProfileService, ProfileService>();  
  25.   
  26.     //...  
  27. }  
  28.   
  29. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)  
  30. {  
  31.     //...  
  32.   
  33.     app.UseIdentityServer();  
  34.   
  35.     JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();  
  36.   
  37.     IdentityServerAuthenticationOptions identityServerValidationOptions = new IdentityServerAuthenticationOptions  
  38.     {  
  39.         //move host url into appsettings.json  
  40.         Authority = "http://localhost:50000/",  
  41.         ApiSecret = "secret",  
  42.         ApiName = "my.api.resource",  
  43.         AutomaticAuthenticate = true,  
  44.         SupportedTokens = SupportedTokens.Both,  
  45.   
  46.         // required if you want to return a 403 and not a 401 for forbidden responses  
  47.         AutomaticChallenge = true,  
  48.   
  49.         //change this to true for SLL  
  50.         RequireHttpsMetadata = false  
  51.     };  
  52.   
  53.     app.UseIdentityServerAuthentication(identityServerValidationOptions);  
  54.   
  55.     //...  
  56. }  

参考文章:

  1. https://stackoverflow.com/questions/35304038/identityserver4-register-userservice-and-get-users-from-database-in-asp-net-core  
钰玺

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: