新文章 网摘 文章 随笔 日记

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

首先定义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  
        {  
            //从你的数据库查询用户信息,或者添加其他的与角色相关的权限验证  
            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);  
  
    //...  
}  

参考文章:

 
  1. https://stackoverflow.com/questions/35304038/identityserver4-register-userservice-and-get-users-from-database-in-asp-net-core  
 
 
在各种客户端中使用的样例:
 
 
 
posted @ 2020-02-17 14:29  岭南春  阅读(212)  评论(0)    收藏  举报