其他分享
首页 > 其他分享> > IdentityServer4之自定义用户数据Claim

IdentityServer4之自定义用户数据Claim

作者:互联网

IdentityServer4之自定义用户数据Claim

 

 

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

 

1. 实现IResourceOwnerPasswordValidator接口

 

 

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

 

/// <summary>

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

/// </summary>

public class CustomResourceOwnerPasswordValidator: IResourceOwnerPasswordValidator

{

/// <summary>

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

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

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

/// </summary>

private readonly TestUserStore _users;

private readonly ISystemClock _clock;

 

public CustomResourceOwnerPasswordValidator(TestUserStore users, ISystemClock clock)

{

_users = users;

_clock = clock;

}

 

/// <summary>

/// 验证

/// </summary>

/// <param name="context"></param>

/// <returns></returns>

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

{

/// <summary>

/// The logger

/// </summary>

protected readonly ILogger Logger;

 

/// <summary>

/// The users

/// </summary>

protected readonly TestUserStore Users;

 

/// <summary>

/// Initializes a new instance of the <see cref="TestUserProfileService"/> class.

/// </summary>

/// <param name="users">The users.</param>

/// <param name="logger">The logger.</param>

public CustomProfileService(TestUserStore users, ILogger<TestUserProfileService> logger)

{

Users = users;

Logger = logger;

}

 

/// <summary>

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

/// </summary>

/// <param name="context">The context.</param>

/// <returns></returns>

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;

}

 

/// <summary>

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

/// </summary>

/// <param name="context">The context.</param>

/// <returns></returns>

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<Claim>(){new Claim(JwtClaimTypes.Role, "admin"),new Claim("性别","男"),new Claim(JwtClaimTypes.Address, "上海")

 

public interface IUserRepository

    {

        List<TestUser> GetTestUsers();

        TestUser GetTestUserByNamePassword(string name,string password);

}

 

public class UserRepository : IUserRepository

    {

 

        List<TestUser> TestUserList = new List<TestUser>

            {

                new TestUser

                {

                    SubjectId = "1",

                    Username = "alice",

                    Password = "password",

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

                },

                new TestUser

                {

                    SubjectId = "2",

                    Username = "bob",

                    Password = "password",

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

                },

                new TestUser

                {

                    SubjectId = "3",

                    Username = "yak",

                    Password = "yakpassword",

                    Claims = new List<Claim>(){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<TestUser> GetTestUsers()

        {

            return TestUserList;

        }

}

 

4. 注册服务

 

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

TestUser对象由IdentityServer4.Test 提供的。

代码如下:

public void ConfigureServices(IServiceCollection services)

        {

            services.AddSingleton<IUserRepository, UserRepository>();

            var builder = services.AddIdentityServer()

                .AddInMemoryIdentityResources(Config.GetIdentityResources())

                .AddInMemoryApiScopes(Config.ApiScopes)

                .AddInMemoryClients(Config.GetClients())

                //.AddTestUsers(Config.GetUsers())

                .AddProfileService<CustomProfileService>()

                .AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator>();

            builder.AddDeveloperSigningCredential();

        }

5. 运行

 

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

 

 

 

 

 

 

 

 

 

6. 使用postman测试

 

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

 

 

 

 

 

 

 

 

7. 使用WinForm测试

 

 

取天气

[HttpGet]

        [Authorize(Roles = "admin")]

        public IEnumerable<WeatherForecast> 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/stulzq/p/8726002.html

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

 

标签:Claim,users,自定义,用户,context,new,public,IdentityServer4
来源: https://www.cnblogs.com/yakniu/p/16182094.html