zoukankan      html  css  js  c++  java
  • Asp.Net Core 中IdentityServer4 授权原理及刷新Token的应用

    一、前言

    上面分享了IdentityServer4 两篇系列文章,核心主题主要是密码授权模式自定义授权模式,但是仅仅是分享了这两种模式的使用,这篇文章进一步来分享IdentityServer4的授权流程及refreshtoken

    系列文章目录(没看过的先看这几篇文章再来阅读本文章):

    为了继续保持IdentityServer4 系列博客分享上下文一致,我这里再把上回授权中心拆分后的图贴出来,如图:

    图中的授权中心就是通过IdentityServer4实现的授权服务中心,我下面就直接用授权中心代替IdentityServer4的授权服务来继续述说,也感谢大家对我的支持,一直阅读我的文章。

    二、授权流程

    2.1 客户端验证流程图

    流程图中,客户端仅仅会到授权中心 请求一次,并拿到验证公钥返回给Api资源拥有端,后面客户端再次尝试请求Api资源时候就不会再到授权中心去获取验证公钥,会直接用之前获取到的公钥进行验证,验证通过则授权通过。

    2.2 授权及刷新refresh_token 流程图

    然而通过授权中心 获取到的access_token 是有有效时间的,如果失效则需要通过refresh_token 重新到授权中心去刷新获取最新的access_token,整体的流程图如下:

    客户端携带上一次获取到的access_token 请求受保护的Api资源时,通过公钥进行验证时发现access_token已经过期,则客户端再携带refresh_token授权中心再次发起请求,刷新access_token以获得最新的access_tokenrefresh_token,用最新的access_token 去获取受保护的Api资源,这样可以减少客户端多次跳转登录授权页面,提高用户体验。

    三、应用实战

    说到例子,我这里不从零开始撸代码, 还是在之前的代码基础上继续改造代码,在原有的定义客户端的代码中新增刷新access_token的相关配置,代码如下:

    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()) },
                 AllowOfflineAccess = true,//如果要获取refresh_tokens ,必须把AllowOfflineAccess设置为true
                 AllowedScopes= {
                     OAuthConfig.UserApi.ApiName,
                     StandardScopes.OfflineAccess,
                 },
                 AccessTokenLifetime = OAuthConfig.ExpireIn,
             },
    
          };
     }
    

    如果你需要刷新access_token,则需要把AllowOfflineAccess设置true,同时添加StandardScopes.OfflineAccess 这个Scopes,主要代码如下:

    AllowOfflineAccess = true,//如果要获取refresh_tokens ,必须把AllowOfflineAccess设置为true
    AllowedScopes= {
         OAuthConfig.UserApi.ApiName,
         StandardScopes.OfflineAccess,//如果要获取refresh_tokens ,必须在scopes中加上OfflineAccess
    },
    

    授权中心,完整代码如下:

    OAuthMemoryData 代码如下:

    /// <summary>
    /// 
    /// </summary>
    public class OAuthMemoryData
    {
            /// <summary>
            /// 资源
            /// </summary>
            /// <returns></returns>
            public static IEnumerable<ApiResource> GetApiResources()
            {
                return new List<ApiResource>
                {
                    new ApiResource(OAuthConfig.UserApi.ApiName,OAuthConfig.UserApi.ApiName),
                };
            }
    
            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()) },
                        AllowOfflineAccess = true,//如果要获取refresh_tokens ,必须把AllowOfflineAccess设置为true
                        AllowedScopes= {
                            OAuthConfig.UserApi.ApiName,
                            StandardScopes.OfflineAccess,
                        },
                        AccessTokenLifetime = OAuthConfig.ExpireIn,
                    },
    
                };
            }
    
            /// <summary>
            /// 测试的账号和密码
            /// </summary>
            /// <returns></returns>
            public static List<TestUser> GetTestUsers()
            {
                return new List<TestUser>
                {
                    new TestUser()
                    {
                         SubjectId = "1",
                         Username = "test",
                         Password = "123456"
                    },
                };
            }
    
            /// <summary>
            /// 微信openId 的测试用户
            /// </summary>
            /// <returns></returns>
            public static List<TestUser> GetWeiXinOpenIdTestUsers()
            {
                return new List<TestUser>
                {
                    new TestUser(){
                      SubjectId="owerhwroogs3902openId",
                    }
                };
            }
        }
    

    Startup 完整代码如下:

     public class Startup
     {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllers();
    
                services.Configure<CookiePolicyOptions>(options =>
                {
                    // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                    options.CheckConsentNeeded = context => true;
                    options.MinimumSameSitePolicy = SameSiteMode.None;
                });
    
    
                #region 内存方式
                //services.AddIdentityServer()
                //    .AddDeveloperSigningCredential()
                //    .AddInMemoryApiResources(OAuthMemoryData.GetApiResources())
                //    .AddInMemoryClients(OAuthMemoryData.GetClients())
                //    .AddTestUsers(OAuthMemoryData.GetTestUsers());
                #endregion
    
                #region 数据库存储方式
                services.AddIdentityServer()
                    .AddDeveloperSigningCredential()
                    .AddInMemoryApiResources(OAuthMemoryData.GetApiResources())
                    //.AddInMemoryClients(OAuthMemoryData.GetClients())
                    .AddClientStore<ClientStore>()
                    .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
                    .AddExtensionGrantValidator<WeiXinOpenGrantValidator>();//添加微信端自定义方式的验证
                #endregion
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseIdentityServer();
    
                app.UseRouting();
                app.UseAuthorization();
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllers();
                });
            }
        }
    

    授权中心代码基本上已经改造完成,我们用postman 访问授权中心 试一试,如下图:

    访问结果中已经包含了refresh_tokenaccess_token等相关信息。

    我们再来通过access_token 访问Api资源(上两篇有相关代码,未阅读上两篇先去查阅)这里我就直接携带access_token去访问,如图:

    访问成功!!

    我们再来刷新下refresh_token ,访问如图:

    刷新refresh_token成功。
    我们到这里再来做一个小小的测试,测试上面的授权流程中的,第4,5 步,上面说到第4步主要是客户端第一次请求Api资源时会向ids4服务网关去请求获取验证公钥,
    获取成功返回给Api资源并存储在内存中,后续不再会到ids4服务去获取验证公钥

    我们把上面的授权中心 (ids4服务网关)停止运行,再来用之前的access_token请求Api资源,如下图:

    现在已经确定授权中心(ids4服务网关)确实停止了,不能访问了,那我们再来通过之前未过期的access_token来请求Api资源网关,结果如下图:

    完美,请求还是成功,这完全证明:客户端请求Api资源网关(受保护的资源)时,第一次收到请求会到授权中心(ids4服务网关)获取验证公钥,并保持到内存中,后面的请求不会再到授权中心去获得验证公钥,而是Api资源网关(受保护的资源)中直接通过保存下来的验证公钥进行验证,从而通过授权

  • 相关阅读:
    数据库
    SqlDataAdapter.Fill
    在应用程序级别之外使用注册为 allowDefinition='MachineToApplication' 的节是错误的。如果在 IIS 中没有将虚拟目录配置为应用程序,则可能导致此错误。
    GridView控件
    已成功与服务器建立连接,但是在登录过程中发生错误
    查看存储过程代码
    vs2005 调试时出现“无法附加。绑定句柄无效”的解决办法
    验证码
    ORACLE的几个函数在MYSQL里面的简单实现
    【100题】第四十五题 雅虎面试两道题(矩阵判断、数组划分)
  • 原文地址:https://www.cnblogs.com/jlion/p/12501195.html
Copyright © 2011-2022 走看看