IdentityServer4一些概念记录

一:基础概念

1、什么是IdentityServer4

IdentityServer4是依赖于asp.net core来实现openId和oauth2.0协议的身份认证框架。

2、OpenId和OAuth2.0概念

要想理解这个之前,先看asp.net core中

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
           ...
            app.UseAuthentication(); //认证,判断是否合法用户
            app.UseAuthorization();  //授权,判断是否有具体功能合法操作权限
           ...
    }

UseAuthentication在UseAuthorization之前

2.1、OpenId

关于这个解释网上有很多,我就不赘述了。按照我的理解,openId关注的是表明用户是谁

2.2、OAuth2.0

OAuth更关注Authorization(授权)。
比如:
比如可以通过A网站授权B网站,获取A网站身份资源,如昵称、头像等信息。

3、IdentityServer4使用场景

  • 单点登录,比如vue、react实现oidc功能
  • 接口保护
  • 支持openId登录
  • 统一登录
  • 自定义登录,比如接入微信的openId登录

4、IdentityServer4主要接口

4.1、获取配置

/.well-known/openid-configuration

4.2、获取加密密钥

/.well-known/openid-configuration/jwks

4.3、获取token

/connect/token

4.4、获取身份信息

/connect/userinfo

IdentityServer4(二)持久化

目录

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

一、简介

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

二、客户端(Clients)

2.1 Clients客户端主表

      是否开启秘钥校验
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(三)配置数据

一、关于API作用域(ApiScope)

一般来说,API作用域是用来规范可访问的API资源,为access_token中scope提供范围声明。
如:

"scope": [
    "gmapiscope",
    "offline_access"
 ],

二、关于API资源(ApiResources)

可访问的api资源列表,每个API资源都需要归纳到某个作用域内(多对多关系)。在ApiResourceScopes表中配置,用来定义哪些apiScope可以访问ApiResourceScope关联的ApiResource。体现在accesstoken_token中aud字段。
如:

"aud": [
    "gmapi",
    "testapi"
 ],

三、关于客户端(Client)

客户端的配置,包括token的过期时间、是否开启刷新token、授权方式、跨域设置、oidc登入登出地址等。注意ClientScopes表,用来定义该客户端可以访问哪些apiScope,简单说,client和ApiResource同时包含某个ApiScope,则client包含该apiscope所有apiResources。

四、关于身份资源(IdentityResources)

用于获取身份信息接口(/connect/userinfo),以后说明。

五、Client、ApiScope、ApiResources关系图及配置顺序

5.1 关系图

 

 

5.2 配置顺序

ApiScope>ApiResources>Client


转自:
作者:我才不是大神
链接:https://www.jianshu.com/p/0c9a9a2b36d6
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
posted @ 2023-08-24 08:51  精耕细琢  阅读(316)  评论(0)    收藏  举报