- A+
首先定义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);
- //...
- }
参考文章:
- https://stackoverflow.com/questions/35304038/identityserver4-register-userservice-and-get-users-from-database-in-asp-net-core