美文网首页
IdentityServer4(二)持久化

IdentityServer4(二)持久化

作者: 我才不是大神 | 来源:发表于2022-08-03 14:09 被阅读0次

    目录

    • 一、简介
    • 二、客户端(Clients)
    • 三、接口资源(ApiResources)
    • 四、接口作用域(ApiScopes)
    • 五、身份资源(IdentityResources)
    • 六、DeviceCodes
    • 七、PersistedGrants
    • 八、aspnet core使用IdentityServer4

    一、简介

    • 这里我对IdentityServer持久化配置中的数据库设计方案进行解释。
    • 数据库使用的是mssql

    二、客户端(Clients)

    客户端数据模型图

    2.1 Clients客户端主表

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int 主键Id
    2 Enabled bit 1 是否有效
    3 ClientId nvarchar 200 客户端Id
    4 ProtocolType nvarchar 200 协议类型(oidc、wsfed、saml2p)
    5 RequireClientSecret bit 1 是否开启秘钥校验
    6 ClientName nvarchar 200 客户端名称
    7 Description nvarchar 1000 客户端说明
    8 ClientUri nvarchar 2000
    9 LogoUri nvarchar 2000
    10 RequireConsent bit 1
    11 AllowRememberConsent bit 1
    12 AlwaysIncludeUserClaimsInIdToken bit 1 始终允许token中包含用户声明信息
    13 RequirePkce bit 1
    14 AllowPlainTextPkce bit 1
    15 RequireRequestObject bit 1
    16 AllowAccessTokensViaBrowser bit 1 是否允许浏览器传递token
    17 FrontChannelLogoutUri nvarchar 2000
    18 FrontChannelLogoutSessionRequired bit 1
    19 BackChannelLogoutUri nvarchar 2000
    20 BackChannelLogoutSessionRequired bit 1
    21 AllowOfflineAccess bit 1 是否允许离线工作,获取刷新令牌需要为true
    22 IdentityTokenLifetime int 身份令牌生命周期(单位:秒)
    23 AllowedIdentityTokenSigningAlgorithms nvarchar 100
    24 AccessTokenLifetime int 访问令牌生命周期(单位:秒)
    25 AuthorizationCodeLifetime int 授权代码生命周期(单位:秒)
    26 ConsentLifetime int 用户同意的生存期(单位:秒)。默认为null(无过期)
    27 AbsoluteRefreshTokenLifetime int 刷新令牌的最长生命周期(单位:秒)
    28 SlidingRefreshTokenLifetime int 刷新令牌滑动过期时间(单位:秒)
    29 RefreshTokenUsage int 刷新令牌使用次数(0-有效期内刷新令牌不变 1-刷新令牌用后即焚)
    30 UpdateAccessTokenClaimsOnRefresh bit 1
    31 RefreshTokenExpiration int 刷新令牌过期方式(0-刷新令牌将在固定时间点到期(由AbsoluteRefreshTokenLifetime指定)1-刷新令牌时,将刷新刷新令牌的生命周期(按SlidingRefreshTokenLifetime中指定的数量)。生命周期不会超过AbsoluteRefreshTokenLifetime)
    32 AccessTokenType int 授权token类型(0-Jwt;1-Reference)
    33 EnableLocalLogin bit 1 登录UI是否呈现用户名/密码输入
    34 IncludeJwtId bit 1
    35 AlwaysSendClientClaims bit 1
    36 ClientClaimsPrefix nvarchar 200 客户端声明类型前缀。默认为client_
    37 PairWiseSubjectSalt nvarchar 200 此客户端用户在成对主体生成中使用的盐值
    38 Created datetime2 创建时间
    39 Updated datetime2 更新时间
    40 LastAccessed datetime2 最后访问时间
    41 UserSsoLifetime int 自上次用户身份验证以来的最长持续时间(单位:秒)
    42 UserCodeType nvarchar 100 设备流用户代码的类型
    43 DeviceCodeLifetime int 设备代码生存期(单位:秒)
    44 NonEditable bit 1

    2.2 ClientClaims 客户端声明配置

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Type nvarchar 250
    3 Value nvarchar 250
    4 ClientId int

    2.3 ClientCorsOrigins 客户端跨域配置

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Origin nvarchar 150
    3 ClientId int

    2.4 ClientGrantTypes 客户端授权模式

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 GrantType nvarchar 250
    3 ClientId int

    2.5 ClientIdPRestrictions(?这个我也不了解,项目中也没有用到)

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Provider nvarchar 200
    3 ClientId int

    2.6 ClientPostLogoutRedirectUris 客户端允许登出回跳地址

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 PostLogoutRedirectUri nvarchar 2000 回跳地址,如:https://xxx/#/signout-callback-out,如果前端使用oidc登录,则需要配置
    3 ClientId int

    2.7 ClientProperties

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Key nvarchar 250
    3 Value nvarchar 2000
    4 ClientId int

    2.8 ClientRedirectUris 客户端允许登录回跳地址

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 RedirectUri nvarchar 2000 回跳地址,如:https://xxx/#/signin-callback-in,如果前端使用oidc登录,则需要配置
    3 ClientId int

    2.9 ClientScopes

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Scope nvarchar 200
    3 ClientId int

    2.10 ClientSecrets 客户端密钥

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Description nvarchar 2000 说明
    3 Value nvarchar 4000 密钥(加密后)
    4 Expiration datetime2 过期时间
    5 Type nvarchar 250 类型(默认SharedSecret)
    6 Created datetime2
    7 ClientId int

    三、接口资源(ApiResources)

    接口资源模型图

    3.1 ApiResources接口资源主表

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Enabled bit 1
    3 Name nvarchar 200
    4 DisplayName nvarchar 200
    5 Description nvarchar 1000
    6 AllowedAccessTokenSigningAlgorithms nvarchar 100
    7 ShowInDiscoveryDocument bit 1
    8 Created datetime2
    9 Updated datetime2
    10 LastAccessed datetime2
    11 NonEditable bit 1

    3.2 ApiResourceClaims接口资源声明表

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Type nvarchar 200
    3 ApiResourceId int

    3.3 ApiResourceProperties接口资源性能表(?还没搞明白用来干啥的)

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Key nvarchar 250
    3 Value nvarchar 2000
    4 ApiResourceId int

    3.4 ApiResourceScopes

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Scope nvarchar 200
    3 ApiResourceId int

    3.5 ApiResourceSecrets

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Description nvarchar 1000
    3 Value nvarchar 4000
    4 Expiration datetime2
    5 Type nvarchar 250
    6 Created datetime2
    7 ApiResourceId int

    四、接口作用域(ApiScopes)

    接口作用域模型图

    4.1 ApiScopes

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Enabled bit 1
    3 Name nvarchar 200
    4 DisplayName nvarchar 200
    5 Description nvarchar 1000
    6 Required bit 1
    7 Emphasize bit 1
    8 ShowInDiscoveryDocument bit 1

    4.2 ApiScopeClaims

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Type nvarchar 200
    3 ScopeId int

    4.3 ApiScopeProperties

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Key nvarchar 250
    3 Value nvarchar 2000
    4 ScopeId int

    五、身份资源(IdentityResources)

    身份资源模型图

    5.1 IdentityResources

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Enabled bit 1
    3 Name nvarchar 200
    4 DisplayName nvarchar 200
    5 Description nvarchar 1000
    6 Required bit 1
    7 Emphasize bit 1
    8 ShowInDiscoveryDocument bit 1
    9 Created datetime2
    10 Updated datetime2
    11 NonEditable bit 1

    5.2 IdentityResourceClaims

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Type nvarchar 200
    3 IdentityResourceId int

    5.3 IdentityResourceProperties

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Id int
    2 Key nvarchar 250
    3 Value nvarchar 2000
    4 IdentityResourceId int

    六、DeviceCodes

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 UserCode nvarchar 200
    2 DeviceCode nvarchar 200
    3 SubjectId nvarchar 200
    4 SessionId nvarchar 100
    5 ClientId nvarchar 200
    6 Description nvarchar 200
    7 CreationTime datetime2
    8 Expiration datetime2
    9 Data nvarchar -1

    七、PersistedGrants

    序号 列名 数据类型 长度 小数位数 主键 自增 允许空 默认值 列说明
    1 Key nvarchar 200
    2 Type nvarchar 50
    3 SubjectId nvarchar 200
    4 SessionId nvarchar 100
    5 ClientId nvarchar 200
    6 Description nvarchar 200
    7 CreationTime datetime2
    8 Expiration datetime2
    9 ConsumedTime datetime2
    10 Data nvarchar max

    八、aspnet core使用IdentityServer4

    8.1、生成证书文件

    我这里使用的是OpenSSL进行生成的。

    req -newkey rsa:2048 -nodes -keyout ids4.key -x509 -days 7300 -out ids4.cer
    pkcs12 -export -in ids4.cer -inkey ids4.key -out ids4.pfx
    

    8.2、写IdentityServer4持久化扩展方法

    using IdentityServer4.EntityFramework.Stores;
    using IdentityServer4.Stores;
    using Ids4.Admin.IdentityServer.Validator;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using System;
    using System.IO;
    using System.Reflection;
    using System.Security.Cryptography.X509Certificates;
    
    namespace Ids4.Admin.IdentityServer.ConfigureServicesExtensions
    {
        public static class ServiceCollectionExtension
        {
            public static IServiceCollection AddIdentityServerPrdExtension(this IServiceCollection services, IConfiguration configuration)
            {
                var dbServer = configuration["Apptions:DBServer"];
                return dbServer switch
                {
                    "mssql" => AddIdentityServerPrdExtension_MsSql(services, configuration),
                    "mysql" => AddIdentityServerPrdExtension_MySql(services, configuration),
                    _ => AddIdentityServerPrdExtension_MsSql(services, configuration),
                };
            }
    
            #region IdentityServer4 配置 MsSql版
    
            /// <summary>
            /// 注册生产环境IdentityServer4配置
            /// </summary>
            /// <param name="services"></param>
            /// <param name="configuration"></param>
            private static IServiceCollection AddIdentityServerPrdExtension_MsSql(IServiceCollection services, IConfiguration configuration)
            {
                //读取证书,我这里使用的pfx证书,可以使用openssl
                var fileName = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, configuration["PfxSettings:Path"]);
                if (!File.Exists(fileName))
                {
                    throw new FileNotFoundException("签名证书文件未找到!");
                }
                var cert = new X509Certificate2(fileName, configuration["PfxSettings:Pwd"]);
                var connectionString = configuration.GetConnectionString("DefaultConnection");
    
                var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
                services.AddIdentityServer()
                    .AddSigningCredential(cert)
                    //客户端及资源数据库存储配置
                    .AddConfigurationStore(options =>
                    {
                        options.ConfigureDbContext = context =>
                            context.UseSqlServer(connectionString,
                                sql => sql.MigrationsAssembly(migrationsAssembly));
                    })
                    //令牌及授权码数据库存储配置
                    .AddOperationalStore(options =>
                    {
                        options.ConfigureDbContext = context =>
                            context.UseSqlServer(connectionString,
                                sql => sql.MigrationsAssembly(migrationsAssembly));
    
                        // 自动清理过期token.
                        options.EnableTokenCleanup = true;
                    })
                    .AddConfigurationStoreCache()//这里使用了内存缓存,避免每次授权都要进行读库
                    .AddResourceOwnerValidator<LoginValidator>()//这里是我实现的自定义用户名密码登陆器
                    .AddExtensionGrantValidator<WeiXinOpenGrantValidator>();//这里是我实现的自定义微信openId登陆器
    
                services.AddTransient<IPersistedGrantStore, PersistedGrantStore>();
                return services;
            }
    
            #endregion
    
            #region IdentityServer4 配置 MySql版
    
            /// <summary>
            /// 注册生产环境IdentityServer4配置
            /// </summary>
            /// <param name="services"></param>
            /// <param name="configuration"></param>
            private static IServiceCollection AddIdentityServerPrdExtension_MySql(IServiceCollection services, IConfiguration configuration)
            {
                var fileName = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, configuration["PfxSettings:Path"]);
                if (!File.Exists(fileName))
                {
                    throw new FileNotFoundException("签名证书文件未找到!");
                }
                var cert = new X509Certificate2(fileName, configuration["PfxSettings:Pwd"]);
                var connectionString = configuration.GetConnectionString("MySqlConnection");
    
                var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
                services.AddIdentityServer()
                    .AddSigningCredential(cert)
                    .AddConfigurationStore(options =>
                    {
                        options.ConfigureDbContext = context =>
                            context.UseMySql(connectionString, new MySqlServerVersion(new Version("5.7")),
                                sql => sql.MigrationsAssembly(migrationsAssembly));
                    })
                    .AddOperationalStore(options =>
                    {
                        options.ConfigureDbContext = context =>
                            context.UseMySql(connectionString, new MySqlServerVersion(new Version("5.7")),
                                sql => sql.MigrationsAssembly(migrationsAssembly));
    
                        // 自动清理过期token.
                        options.EnableTokenCleanup = true;
                    })
                    .AddConfigurationStoreCache()//这里使用了内存缓存,避免每次授权都要进行读库
                    .AddResourceOwnerValidator<LoginValidator>()
                    .AddExtensionGrantValidator<WeiXinOpenGrantValidator>(); 
    
                services.AddTransient<IPersistedGrantStore, PersistedGrantStore>();
                return services;
            }
    
            #endregion
        }
    }
    
      public void ConfigureServices(IServiceCollection services)
       {
                services.AddIdentityServerPrdExtension(Configuration);//注册IdentityServer4持久化
       }
    

    8.3、自定义登录验证器

    8.3.1 密码模式验证器

    官方或者各大百度找到的示例,几乎都是使用IdentityServer4的TestUser,在生产中,这种写法肯定不行。上述代码中有LoginValidator,这个就是我自定义的密码登录验证器,主要功能就是替换默认的密码模式登录。

    /// <summary>
    /// 密码模式验证器
    /// </summary>
    /// <remarks>需要继承IResourceOwnerPasswordValidator,在注册IdentityServer4时,通过AddResourceOwnerValidator注册验证器</remarks>
    public class LoginValidator : IResourceOwnerPasswordValidator
    {
        private readonly ISystemService _systemService;
    
        public LoginValidator(ISystemService systemService)
        {
            _systemService = systemService;
        }
    
        public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
        {
            //用户密码登录,这个可以自己实现
            var (userInfo, errMsg) = await _systemService.LgoinAsync(new LoginReques()
            {
                Account = context.UserName,
                Password = context.Password,
            });
            context.Result = UserGrantResult.GetGrantValidationResult(userInfo, errMsg);
        }
    }
    
    public class UserGrantResult
    {
        public static GrantValidationResult GetGrantValidationResult(LoginResponse user, string errMsg = "")
        {
            if (user != null)
            {
                Claim[] claims = new Claim[] {
                    new Claim("employeeId",user.EmployeeId.ToString()),
                    new Claim("tenantId",user.TenantId.ToString()),
                    //这里可以写自己所需的claim
                };
                return new GrantValidationResult(subject: user.EmployeeId.ToString(), authenticationMethod: "custom", claims: claims);
            }
            else
            {
                return new GrantValidationResult(TokenRequestErrors.InvalidRequest, string.IsNullOrWhiteSpace(errMsg) ? "用户名或密码错误" : errMsg);
            }
        }
    }
    
    

    8.3.2 自定义微信登录模式

    有些业务可能会需要微信一键登录吧,IdentityServer4可以通过自定义扩展授权模式,来实现微信openId的一键登录

        /// <summary>
        /// 微信授权验证
        /// </summary>
        /// <remarks>需要继承IExtensionGrantValidator,在注册IdentityServer4时,通过AddExtensionGrantValidator注册验证器</remarks>
        public class WeiXinOpenGrantValidator : IExtensionGrantValidator
        {
            private readonly IUserService _userService;
    
            public WeiXinOpenGrantValidator(IUserService userService)
            {
                _userService = userService;
            }
    
            public string GrantType => "wechatopen";//定义微信登录模式名称
    
            /// <summary>
            /// 验证器
            /// </summary>
            /// <param name="context"></param>
            /// <returns></returns>
            public async Task ValidateAsync(ExtensionGrantValidationContext context)
            {
                try
                {
                    #region 获取参数
                    var appid = context.Request.Raw["appid"];//微信AppId
                    var openId = context.Request.Raw["openid"];//用户OpenId
                    var phone = context.Request.Raw["phone"];//用户手机号码
                    #endregion
    
                    var (userInfo, errMsg) = await _userService.QueryAccountByWechatOpenIdAsync(appid, openId, phone);//自定义微信登录方法
                    context.Result = UserGrantResult.GetGrantValidationResult(userInfo, errMsg);
                }
                catch (Exception ex)
                {
                    context.Result = new GrantValidationResult()
                    {
                        IsError = true,
                        Error = ex.Message
                    };
                }
            }
        }
    

    参考8.2,注册IdentityServer4持久化时,将自定义验证器也注册进去就行。

    8.4 客户端使用IdentityServer4

    8.4.1 API使用IdentityServer4

    public static IServiceCollection AddIds4Client_API(this IServiceCollection services, ApiAuthenticationModel model)
    {
            ervices.AddAuthorization();
            ervices.AddAuthentication("Bearer")
                .AddIdentityServerAuthentication(options =>
                {
                    /*===========================================
                    这里填的是签名颁发机构地址,会调用//.well-known/openid-configuration/jwks 获取签名服务器公钥,
                    是通过访问/.well-known/openid-configuration,获取到所需信息
                    ===========================================*/
                    options.Authority = model.Authority;
                    options.RequireHttpsMetadata = model.RequireHttpsMetadata;           //是否开启https    
                    options.ApiName = model.ApiName;  //api的name,
                    options.ApiSecret = model.ApiSecret;//api密钥
                });
            return services;
    }
    
    
        public class ApiAuthenticationModel
        {
            /// <summary>
            /// 验证地址
            /// </summary>
            public string Authority { get; set; }
    
            /// <summary>
            /// 是否Https请求
            /// </summary>
            public bool RequireHttpsMetadata { get; set; }
    
            /// <summary>
            /// 客户端ID名称
            /// </summary>
            public string ApiName { get; set; }
    
            /// <summary>
            /// 客户端密钥
            /// </summary>
            public string ApiSecret { get; set; }
        }
    

    接下来会开一篇新的文章,讲述各种IdentityServer4配置关系

    相关文章

      网友评论

          本文标题:IdentityServer4(二)持久化

          本文链接:https://www.haomeiwen.com/subject/yggswrtx.html