ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

IdentityServer4之自定义用户数据Claim

2022-04-23 12:32:06  阅读:211  来源: 互联网

标签:Claim users 自定义 用户 context new public IdentityServer4


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有