首页 > 其他分享 >在IdentityServer4生成的JWT中添加一个自定义的Claim,用于ABP框架中要用到的token信息

在IdentityServer4生成的JWT中添加一个自定义的Claim,用于ABP框架中要用到的token信息

时间:2024-06-22 11:20:47浏览次数:13  
标签:Claim 中要 自定义 user context var new public

用过IdentityServer4或者熟悉ASP.NET Core认证的都应该知道有Claim,如何理解ids4中的Claim?

这里可以理解为声明,我们每个用户都有多个Claim,每个Claim声明了用户的某个信息比如:Role=Admin,UserID=1000等等,这里Role,UserID每个都是用户的Claim,都是表示用户信息的单元 ,我们不妨把它称为用户信息单元 。

Claim相关的解析 http://www.cnblogs.com/savorboard/p/aspnetcore-identity.html

需求:在ids4中加一个登录用户所属的公司ID(companyid),默认生成的token信息中的用户信息单元,一般包含如下信息:用户名、邮箱地址、电话号、角色信息等基本的用户单元信息

public virtual async Task CreateStandardResourcesAsync()
{
    var resources = new[]
    {
            new IdentityServer4.Models.IdentityResources.OpenId(),
            new IdentityServer4.Models.IdentityResources.Profile(),
            new IdentityServer4.Models.IdentityResources.Email(),
            new IdentityServer4.Models.IdentityResources.Address(),
            new IdentityServer4.Models.IdentityResources.Phone(),
            new IdentityServer4.Models.IdentityResource("role", "Roles of the user", new[] {"role"})
        };

    foreach (var resource in resources)
    {
        foreach (var claimType in resource.UserClaims)
        {
            await AddClaimTypeIfNotExistsAsync(claimType);
        }

        await AddIdentityResourceIfNotExistsAsync(resource);
    }
}

解析生成的token

 如何在解析的token中加入一个自定义的claim?比如添加一个公司ID,有两种实现方式:

方法一

1. **创建自定义声明(Claim)**:
首先,需要在用户登录的时候,将`company`信息添加到用户的声明中。你可以在你的用户存储(例如,数据库)中存储`company`信息,并在用户登录时将其添加到用户的声明中。

2. **扩展ProfileService**:
你需要创建一个自定义的`ProfileService`,以便在生成JWT时包含自定义的声明。

 public class UserProfileService : IProfileService
 {
     protected IdentityUserManager UserManager { get; }

     public UserProfileService(IdentityUserManager userManager)
     {
         UserManager = userManager;
     }

     public async Task GetProfileDataAsync(ProfileDataRequestContext context)
     {
         var user = await UserManager.GetUserAsync(context.Subject);
         if (user != null)
         {
             var claims = new List<Claim>
         {
             new Claim("companyid", "11111"),
             new Claim("name",user.Name),
             new Claim("user_name",user.UserName),
             new Claim("email",user.Email),
             new Claim("email_verified",user.EmailConfirmed.ToString()),
             new Claim("phone_number",user.PhoneNumber),
             new Claim("phone_number_verified",user.PhoneNumberConfirmed.ToString())
         };
             // 确保添加用户的角色Claim
             var roles = await UserManager.GetRolesAsync(user);
             foreach (var role in roles)
             {
                 claims.Add(new Claim("role", role));
             }

             context.IssuedClaims.AddRange(claims);
         }
     }

     public async Task IsActiveAsync(IsActiveContext context)
     {
         var user = await UserManager.GetUserAsync(context.Subject);
         context.IsActive = user != null;
     }
 }

3. **配置IdentityServer4**:

将自定义的`ProfileService`注册到IdentityServer4中。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // 其他服务配置...

        // 注册自定义的Profile服务
        services.AddTransient<IProfileService, UserProfileService>();

        // IdentityServer的其他配置...
    }
}

方法二

参照APB官网给的示例,来实现,推荐方法

参见地址:
https://docs.abp.io/en/abp/latest/Authorization#claims-principal-factory
public class SocialSecurityNumberClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
{
    public async Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
    {
        var identity = context.ClaimsPrincipal.Identities.FirstOrDefault();
        var userId = identity?.FindUserId();
        if (userId.HasValue)
        {
            var userService = context.ServiceProvider.GetRequiredService<IUserService>(); //Your custom service
            var socialSecurityNumber = await userService.GetSocialSecurityNumberAsync(userId.Value);
            if (socialSecurityNumber != null)
            {
                identity.AddClaim(new Claim("SocialSecurityNumber", socialSecurityNumber));
            }
        }
    }
}

Configure<AbpClaimsServiceOptions>(options=>
{
    options.RequestedClaims.Add("SocialSecurityNumber")
})

public static class CurrentUserExtensions
{
    public static string GetSocialSecurityNumber(this ICurrentUser currentUser)
    {
        return currentUser.FindClaimValue("SocialSecurityNumber");
    }
}

模拟方法如下:

using AuthServer.Host.Dappers;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.Security.Claims;
using Volo.Abp.Users;
using static Volo.Abp.Identity.Settings.IdentitySettingNames;

namespace AuthServer.Host
{
    public class Config
    {
        public static IEnumerable<IdentityServer4.Models.IdentityResource> GetIdentityResourceResources()
        {
            var customProfile = new IdentityServer4.Models.IdentityResource(
                name: "custom.profile",
                displayName: "Custom profile",
                userClaims: new[] { "companyid" });

            return new List<IdentityServer4.Models.IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                customProfile
            };
        }
    }

    public class UserProfileService : IProfileService
    {
        protected IdentityUserManager UserManager { get; }

        public UserProfileService(IdentityUserManager userManager)
        {
            UserManager = userManager;
        }

        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            var user = await UserManager.GetUserAsync(context.Subject);
            if (user != null)
            {
                var claims = new List<Claim>
            {
                new Claim("companyid", "11111"),
                new Claim("name",user.Name),
                new Claim("user_name",user.UserName),
                new Claim("email",user.Email),
                new Claim("email_verified",user.EmailConfirmed.ToString()),
                new Claim("phone_number",user.PhoneNumber),
                new Claim("phone_number_verified",user.PhoneNumberConfirmed.ToString())
            };
                // 确保添加用户的角色Claim
                var roles = await UserManager.GetRolesAsync(user);
                foreach (var role in roles)
                {
                    claims.Add(new Claim("role", role));
                }

                context.IssuedClaims.AddRange(claims);
            }
        }

        public async Task IsActiveAsync(IsActiveContext context)
        {
            var user = await UserManager.GetUserAsync(context.Subject);
            context.IsActive = user != null;
        }
    }

    public class SocialSecurityNumberClaimsPrincipalContributor : IAbpClaimsPrincipalContributor, ITransientDependency
    {
        private readonly DapperDbContext _dbDapperContext;
        protected IdentityUserManager UserManager { get; }
        public SocialSecurityNumberClaimsPrincipalContributor(DapperDbContext dbDapperContext, IdentityUserManager userManager)
        {
            _dbDapperContext = dbDapperContext;
            UserManager = userManager;
        }
        public async Task ContributeAsync(AbpClaimsPrincipalContributorContext context)
        {
            var identity = context.ClaimsPrincipal.Identities.FirstOrDefault();
            var userId = identity?.FindUserId();
            if (userId.HasValue)
            {
                var user = await UserManager.GetUserAsync(context.ClaimsPrincipal);
                identity.AddClaim(new Claim("username", user.Name));//登录用户名
                string sql = @$"SELECT OrganizationId FROM [dbo].[base_user_orgs] WHERE UserId=('{userId}') ";
                var org=await _dbDapperContext.QueryAsync<Guid>(sql, databaseType: DatabaseType.Default);
                var companyId = org.Any() ? string.Join(",",org.ToList()) : "";//会有同一个属于多个公司
                identity.AddClaim(new Claim("companyid", companyId));//测试用
                //var userService = context.ServiceProvider.GetRequiredService<IUserService>(); //Your custom service
                //var socialSecurityNumber = await userService.GetSocialSecurityNumberAsync(userId.Value);
                //if (socialSecurityNumber != null)
                //{
                //    identity.AddClaim(new Claim("SocialSecurityNumber", socialSecurityNumber));
                //}
            }
        }
    }

    public static class CurrentUserExtensions
    {
        public static string GetSocialSecurityNumber(this ICurrentUser currentUser)
        {
            return currentUser.FindClaimValue("companyid");
        }
    }
}

生成的实际效果

 

 

标签:Claim,中要,自定义,user,context,var,new,public
From: https://www.cnblogs.com/netcore-vue/p/18262020

相关文章

  • 对比Vue2/Vue3项目如何自定义插件
    学习目标:对比Vue2/Vue3项目如何自定义插件学习内容:插件(Plugins)是一种能为Vue添加全局功能的工具代码。一个插件可以是一个拥有 install() 方法的对象,也可以直接是一个安装函数本身。安装函数会接收到安装它的应用实例传递给Vue.use()/ app.use() 的额外选项作......
  • SpringBoot+AOP+Redis自定义注解实现防重复提交
    1.哪些因素会引起重复提交?开发项目过程中可能会出现下面这些情况:前端下单按钮重复点击导致订单创建多次网速等原因造成页面卡顿,用户重复刷新提交请求黑客或恶意用户使用Postman等http工具重复恶意提交表单2.重复提交会带来哪些问题?重复提交带来的问题:会导致数据......
  • UE5笔记-实现Lumen实时渲染GI下的的类UCanvasRenderTarget实现多场景/自定义分辨率/方
    默认的SceneCapture不能用于实时Lumen光照模式下为了实现实时渲染GI下的的类似于UCanvasRenderTarget2D类.可以参考GameViewport类的源码尝试使用UE的渲染逻辑和数据多渲染一份视口副本到直接的FSceneView上,封装一份UCaptureRenderTarget出来从而实现一些例如自定义分辨率的......
  • 一文读懂Java线程池之自定义线程池、设置合适的线程数量、线程池阻塞队列、线程拒绝策
    在上篇我们学习了线程池各个参数的含义,线程池任务处理流程,使用线程池的好处等内容,本篇我们学习如何创建一个适合我们业务的线程池。为此,我们有必要先学习一下如何大概确定我们线程池核心线程数、怎么设置阻塞队列的类型与大小、当线程池没有能力处理任务了该如何使用拒绝策略等......
  • sqlalchemy根据字典kv自定义表结构
    根据数据的内容自动创建数据库表结构fromsqlalchemyimportcreate_engine,Column,Integer,String,Float,Booleanfromsqlalchemy.ext.declarativeimportdeclarative_basefromsqlalchemy.ormimportsessionmaker,Mapped,mapped_columnBase=declarative_base()......
  • 自定义组件获取接口数据
    <template><divclass="ting-title"><spanclass="text">{{text}}</span></div></template><script>exportdefault{name:'TingTitle',data:()=>{......
  • 微信小程序学习(七):自定义组件和通信
    1、基本知识开发中常见的组件主要分为公共组件和页面组件两种,因此注册组件的方式也分为两种:全局注册:在app.json文件中配置usingComponents节点进行引用声明,注册后可在任意组件使用局部注册:在页面.json文件中配置usingComponents节点进行引用声明,只可在当前页面......
  • apisix~为自定义插件设计一个configmap脚本
    configMapKubernetes中的ConfigMap是一种用来存储配置数据的API资源,它允许您将配置信息以键值对的形式保存,并在容器中使用这些配置信息。ConfigMap提供了一种将配置数据与应用程序解耦的方式,使得应用程序可以动态地获取配置而无需重新构建镜像。以下是ConfigMap的一些特......
  • ef core自定义默认的迁移表的名称
    efcore自定义默认的迁移表的名称usingMicrosoft.EntityFrameworkCore;usingMicrosoft.EntityFrameworkCore.Design;namespaceLong.EntityFrameworkCore;classMyDesignTimeDbContextFactory:IDesignTimeDbContextFactory<ApplicationDbContext>{publicApplicat......
  • 自定义类型:结构体,枚举,联合
    目录一.结构体1.0 C语言中为什么要引入结构体?1.1 结构体的声明1.2特殊的声明1.3结构体变量的定义和初始化二.枚举1.0类型的定义 1.1我们可以使用#define定义常量,为什么非要使用枚举?三.联合(共用体)1.0联合类型的定义1.1 联合的声明一.结构体1.0 C......