Asp.Net Core 中IdentityServer4 受权中心之自定义受权模式

1、前言

上一篇我分享了一篇关于 Asp.Net Core 中IdentityServer4 受权中心之应用实战 的文章,其中有很多博友给我提了问题,其中有一个博友问个人一个场景,我给他解答的还不够完美,以后我通过本身的学习查阅并阅读了相关源代码,发现 IdentityServer4 能够实现自定义GrantType 受权方式。html

声明:看这篇文章时若是你没有阅读我上一篇 Asp.Net Core 中IdentityServer4 受权中心之应用实战 的文章,那请先移步看上面的文章,再来看这篇文章会更加清晰,感谢支持,感谢关注!git

2、场景模拟

上篇文章已经把电商系统从单一网关架构升级到多网关架构,架构图以下:
github

然而上面的受权中心 使用的是密码受权模式,可是对于微信小程序微信公众号商城端使用的受权还不是很合适;
微信小程序微信公众号微商城客户端的场景以下:
用户访问小程序商城或者微信公众号商城后会到微信服务端得到受权拿到相关的用户openIdunionIduserName 等相关信息,再携带openIdunionIduserName等信息访问受权中心网关,进行受权,若是不存在则自动注册用户,若是存在则登陆受权成功等操做。那这个场景后我该如何改造受权中心服务网关呢?通过研究和探讨,我把上面的架构图细化成以下的网关架构图:
数据库

3、受权中心改造升级

上一篇文章中咱们的解决方案中已经创建了三个项目:小程序

  • Jlion.NetCore.Identity.Service受权中心 网关 - WebApi 项目
  • Jlion.NetCore.Identity.UserApiService用户业务网关 -WebApi项目
  • Jlion.NetCore.Identity基础类库,主要用于把公共的基础设施层放到这一块

经过上面的需求场景分析,咱们目前的受权中心还不够这种需求,故咱们能够经过IdentityServer4 自定义受权方式进行改造升级来知足上面的场景需求。微信小程序

通过查看源代码我发现咱们能够经过实现IExtensionGrantValidator抽象接口进行自定义受权方式来实现,而且实现ValidateAsync 方法,
如今我在以前的解决方案受权中心项目中新增WeiXinOpenGrantValidator类代码以下:安全

public class WeiXinOpenGrantValidator : IExtensionGrantValidator
{
  public string GrantType => GrantTypeConstants.ResourceWeixinOpen;

  public async Task ValidateAsync(ExtensionGrantValidationContext context)
  {
      try
      {
         #region 参数获取
         var openId = context.Request.Raw[ParamConstants.OpenId];
         var unionId = context.Request.Raw[ParamConstants.UnionId];
         var userName = context.Request.Raw[ParamConstants.UserName];
         #endregion

         #region 经过openId和unionId 参数来进行数据库的相关验证
         var claimList = await ValidateUserAsync(openId, unionId);
         #endregion

         #region 受权经过
         //受权经过返回
         context.Result = new GrantValidationResult
         (
             subject: openId,
             authenticationMethod: "custom",
             claims: claimList.ToArray()
         );
         #endregion
     }
     catch (Exception ex)
     {
         context.Result = new GrantValidationResult()
         {
             IsError = true,
             Error = ex.Message
         };
     }
  }

   #region Private Method
   /// <summary>
   /// 验证用户
   /// </summary>
   /// <param name="loginName"></param>
   /// <param name="password"></param>
   /// <returns></returns>
   private async Task<List<Claim>> ValidateUserAsync(string openId, string unionId)
   {
      //TODO 这里能够经过openId 和unionId 来查询用户信息(数据库查询),
      //我这里为了方便测试仍是直接写测试的openId 相关信息用户
      var user = OAuthMemoryData.GetWeiXinOpenIdTestUsers();

      if (user == null)
      {
         //注册用户
      }

      return new List<Claim>()
      {
          new Claim(ClaimTypes.Name, $"{openId}"),
      };
   }
   #endregion
 }

GrantTypeConstants 代码是静态类,主要用于定义GrantType的自定义受权类型,可能后续还有更多的自定义受权方式因此,统一放这里面进行管理,方便维护,代码以下:微信

public static class GrantTypeConstants
 {
     /// <summary>
     /// GrantType - 微信端受权
     /// </summary>
     public const string ResourceWeixinOpen = "weixinopen";
 }

ParamConstants 类主要是定义自定义受权须要的参数,代码以下:架构

public class ParamConstants
{
    public const string OpenId = "openid";

    public const string UnionId = "unionid";

    public const string UserName = "user_name";
}

好了上面得自定义验证器已经实现了,可是还不够,咱们还须要让客户端支持自定义的受权类型,咱们打开OAuthMemoryData代码中的GetClients,代码以下:async

public static IEnumerable<Client> GetClients()
{
    return new List<Client>
    {
        new Client()
        {
            ClientId =OAuthConfig.UserApi.ClientId,
            AllowedGrantTypes = new List<string>()
            {
                GrantTypes.ResourceOwnerPassword.FirstOrDefault(),//Resource Owner Password模式
                GrantTypeConstants.ResourceWeixinOpen,//新增的自定义微信客户端的受权模式
            },
            ClientSecrets = {new Secret(OAuthConfig.UserApi.Secret.Sha256()) },
            AllowedScopes= {OAuthConfig.UserApi.ApiName},
            AccessTokenLifetime = OAuthConfig.ExpireIn,
        },
                
    };
}

客户端AllowedGrantTypes 配置新增了我刚刚自定义的受权方式GrantTypeConstants.ResourceWeixinOpen,
如今客户端的支持也已经配置好了,最后咱们须要经过AddExtensionGrantValidator<>扩展方法把自定义受权验证器注册到DI中,代码以下:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();


    #region 数据库存储方式
    services.AddIdentityServer()
        .AddDeveloperSigningCredential()
        .AddInMemoryApiResources(OAuthMemoryData.GetApiResources())
        //.AddInMemoryClients(OAuthMemoryData.GetClients())
        .AddClientStore<ClientStore>()
        .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
        .AddExtensionGrantValidator<WeiXinOpenGrantValidator>();
    #endregion
 }

好了,简单的受权中心代码升级已经完成,咱们分别经过命令行运行受权中心用户业务网关 ,以前的用户业务网关无需改动任何代码,运行图分别以下:

Jlion.NetCore.Identity.Server 受权中心运行以下

Jlion.NetCore.Identity.UserApiServer 用户业务网关运行以下

咱们如今用postman模拟openIdunionIduserName参数来请求受权中心得到AccessToken,请求以下:

咱们再经过postman 携带受权信息访问用户业务网关数据,结果图以下:

好了,自定义受权模式已经完成,简单的受权中心也已经升级完成,上面WeiXinOpenGrantValidator验证器中我没有直接走数据库方式进行验证和注册,简单的写了个Demo ,你们有兴趣能够 把TODO那一快数据库的操做去实现,代码我已经提交到 github上了,这里再次分享下我博客同步实战的demo 地址 https://github.com/a312586670/IdentityServerDemo

4、思考与总结

本篇我介绍了自定义受权方式,经过查看源代码及查阅资料学习了IdentityServer4 能够经过自定义受权方式进行扩展。这样受权中心能够扩展多套受权方式,好比今天所分享的 自定义微信openId 受权、短信验证码受权等其余自定义受权,一套Api资源能够兼并多套受权模式,灵活扩展,灵活升级。本篇涉及的知识点很少,可是很是重要,由于咱们在使用受权中心统一身份认证时常常会遇到多种认证方式的结合,和多套不一样应用用户的使用,在掌握了受权原理后,就能在不一样的受权方式中切换的游刃有余,到这里有的博友会问AccentToken 有过时时间,会过时怎么办?难道要从新受权一次吗?这些问题我会安排下一篇文章分享。

灵魂一问:

上面的受权中心 例子主要是为了让你们更好的理解自定义受权的使用场景及它的灵活性,真实的场景这样直接把 openId等相关信息来验证受权安全吗?你们能够能够思考下,若是不安全你们又有什么好的解决方案呢?自我提高在于不停的自我思考,你们能够敬请的发挥本身的思考,把答案留在留言板中,以供你们参考学习,感谢!!!

相关文章
相关标签/搜索