IdentityServer4之自定义用户数据Claim


IdentityServer4之自定义用户数据Claim

前面章节案例是使用TestUsers是用于测试的,而通常系统一般都要接入来源于数据库中的已有用户,需实现IProfileServiceIResourceOwnerPasswordValidator接口。

1. 实现IResourceOwnerPasswordValidator接口

实现IResourceOwnerPasswordValidator接口,来定义我们自己的验证逻辑。

/// 

/// 自定义 Resource owner password 验证器

/// 

public class CustomResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator

/// 

/// 这里为了演示我们还是使用TestUser作为数据源,

/// 正常使用此处应当传入一个 用户仓储 等可以从

/// 数据库或其他介质获取我们用户数据的对象

/// 

private readonly TestUserStore _users;

private readonly ISystemClock _clock;

public CustomResourceOwnerPasswordValidator(TestUserStore users, ISystemClock clock)

_users = users;

_clock = clock;

/// 

/// 验证

/// 

/// 

/// 

public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)

//此处使用context.UserName, context.Password 用户名和密码来与数据库的数据做校验

if (_users.ValidateCredentials(context.UserName, context.Password))

var user = _users.FindByUsername(context.UserName);

//验证通过返回结果

//subjectId 为用户唯一标识 一般为用户id

//authenticationMethod 描述自定义授权类型的认证方法

//authTime 授权时间

//claims 需要返回的用户身份信息单元 此处应该根据我们从数据库读取到的用户信息 添加Claims 如果是从数据库中读取角色信息,那么我们应该在此处添加

context.Result = new GrantValidationResult(

user.SubjectId ?? throw new ArgumentException("Subject ID not set", nameof(user.SubjectId)),

OidcConstants.AuthenticationMethods.Password, _clock.UtcNow.UtcDateTime,

user.Claims);

else

//验证失败

context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential");

return Task.CompletedTask;

2. 实现IProfileService接口

 

实现了IResourceOwnerPasswordValidator还不够,我们还需要实现IProfileService接口,他是专门用来装载我们需要的Claim信息的,比如在token创建期间和请求用户信息终结点是会调用它的GetProfileDataAsync方法来请求需要的Claim类型装载信息,下面是一个简单实现:

public class CustomProfileService: IProfileService

/// 

/// The logger

/// 

protected readonly ILogger Logger;

/// 

/// The users

/// 

protected readonly TestUserStore Users;

/// 

/// Initializes a new instance of the  class.

/// 

/// The users.

/// The logger.

public CustomProfileService(TestUserStore users, ILogger logger)

Users = users;

Logger = logger;

/// 

/// 只要有关用户的身份信息单元被请求(例如在令牌创建期间或通过用户信息终点),就会调用此方法

/// 

/// The context.

/// 

public virtual Task GetProfileDataAsync(ProfileDataRequestContext context)

context.LogProfileRequest(Logger);

//判断是否有请求Claim信息

if (context.RequestedClaimTypes.Any())

//根据用户唯一标识查找用户信息

var user = Users.FindBySubjectId(context.Subject.GetSubjectId());

if (user != null)

//调用此方法以后内部会进行过滤,只将用户请求的Claim加入到 context.IssuedClaims 集合中 这样我们的请求方便能正常获取到所需Claim

context.AddRequestedClaims(user.Claims);

context.LogIssuedClaims(Logger);

return Task.CompletedTask;

/// 

/// 验证用户是否有效 例如:token创建或者验证

/// 

/// The context.

/// 

public virtual Task IsActiveAsync(IsActiveContext context)

Logger.LogDebug("IsActive called from: {caller}", context.Caller);

var user = Users.FindBySubjectId(context.Subject.GetSubjectId());

context.IsActive = user?.IsActive == true;

return Task.CompletedTask;

IResourceOwnerPasswordValidator 是为了对接已有的用户数据,然后才是实现 IProfileService 以添加自定义 claim

3. 添加用户接口

这里模拟从数据库读取用户数据的仓储接口IUserRepository及实现类UserRepository。

Claims = new List(){new Claim(JwtClaimTypes.Role, "admin"),new Claim("性别","男"),new Claim(JwtClaimTypes.Address, "上海")

public interface IUserRepository

    {

        List GetTestUsers();

        TestUser GetTestUserByNamePassword(string name,string password);

public class UserRepository : IUserRepository

    {

        List TestUserList = new List

            {

                new TestUser

                {

                    SubjectId = "1",

                    Username = "alice",

                    Password = "password",

                    Claims = new List(){new Claim(JwtClaimTypes.Role,"superadmin") }

                },

                new TestUser

                {

                    SubjectId = "2",

                    Username = "bob",

                    Password = "password",

                    Claims = new List(){new Claim(JwtClaimTypes.Role,"superadmin") }

                },

                new TestUser

                {

                    SubjectId = "3",

                    Username = "yak",

                    Password = "yakpassword",

                    Claims = new List(){new Claim(JwtClaimTypes.Role, "admin"),new Claim("性别","男"),new Claim(JwtClaimTypes.Address, "上海") }

                }

            };

        public TestUser GetTestUserByNamePassword(string name, string password)

        {

            var qure = TestUserList.Where(i => i.Username == name && i.Password == password).FirstOrDefault();

            return qure;

        }

        public List GetTestUsers()

        {

            return TestUserList;

        }

4. 注册服务

 

Startup类里启用我们自定义的ProfileService和ResourceOwnerValidator,并且在Startup配置Service时不再需要AddTestUsers,因为将使用我们自己的用户信息,而

TestUser对象由IdentityServer4.Test 提供的。

代码如下:

public void ConfigureServices(IServiceCollection services)

        {

            services.AddSingleton();

            var builder = services.AddIdentityServer()

                .AddInMemoryIdentityResources(Config.GetIdentityResources())

                .AddInMemoryApiScopes(Config.ApiScopes)

                .AddInMemoryClients(Config.GetClients())

                //.AddTestUsers(Config.GetUsers())

                .AddProfileService()

                .AddResourceOwnerValidator();

            builder.AddDeveloperSigningCredential();

        }

5. 运行

选择多个启动项目,运行。

6. 使用postman测试

获取token,然后获取用户的信息。

7. 使用WinForm测试

取天气

[HttpGet]

        [Authorize(Roles = "admin")]

        public IEnumerable Get()

        {

            var rng = new Random();

            return Enumerable.Range(1, 5).Select(index => new WeatherForecast

            {

                Date = DateTime.Now.AddDays(index),

                TemperatureC = rng.Next(-20, 55),

                Summary = Summaries[rng.Next(Summaries.Length)]

            })

            .ToArray();

        }

取用户信息

[Authorize(Roles = "admin")]

        [HttpGet]

        public IActionResult GetClaims()

        {

            return new JsonResult(from c in HttpContext.User.Claims select new { c.Type, c.Value });

        }

鸣谢

https://gitee.com/github_mirrors/identityserver4_doc.zh-cn

https://www.cnblogs.com/sheng-jie/p/9430920.html

相关