zoukankan      html  css  js  c++  java
  • IdentityServer4 (3) 授权码模式(Authorization Code)

    写在前面

    1、源码(.Net Core 2.2)

      git地址:https://github.com/yizhaoxian/CoreIdentityServer4Demo.git

    2、相关章节

      2.1、《IdentityServer4 (1) 客户端授权模式(Client Credentials)
      2.2、《IdentityServer4 (2) 密码授权(Resource Owner Password)
      2.3、《IdentityServer4 (3) 授权码模式(Authorization Code)
      2.4、《IdentityServer4 (4) 静默刷新(Implicit)
      2.5、《IdentityServer4 (5) 混合模式(Hybrid)

    3、参考资料

      IdentityServer4 中文文档 http://www.identityserver.com.cn/
      IdentityServer4 英文文档 https://identityserver4.readthedocs.io/en/latest/
      OpenID Connect 官网 https://openid.net/connect/
      OpenID Connect 中文 https://www.cnblogs.com/linianhui/p/openid-connect-core.html
      OpenID Connect和OAuth 2.0对比:https://www.jianshu.com/p/d453076e6433
      Oauth 2.0 官网:https://oauth.net/2/
      Oauth 2.0 授权框架:https://tools.ietf.org/html/rfc6749#section-4.2.1

    4、流程图

      1、访问客户端受保护的url
      2、跳转到授权服务器认证
      3、用户填写认证(账号密码)
      4、认证成功,选择授权的scope
      5、授权成功(点击同意授权),携带授权码code返回客户端
      6、客户端通过后台通信请求AccessToken 和IdToken,如果设置了 OfflineAccess=true也会返回RefreshToken(可以刷新AccessToken)

      整个访问流程建议使用fiddler 抓包查看,更好的了解

    一、服务端

    1、下载一个官方示例

      地址:https://github.com/IdentityServer/IdentityServer4.Quickstart.UI 

      根据自己使用的Core 版本下载对应的,点击 tags 里找,我下载的是2.5,解压后如下图

      

    2、新建一个 web 项目 .NetCore 2.2版本

       把官网下载的文件添加到项目中,Quickstart 我换成了Controllers

    3、添加配置类 (IdpConfig.cs)

      参看 《IdentityServer4 (1) 客户端授权模式(Client Credentials)》里的,直接复制过来就可以了

    4、添加客户端

      IdpConfig.cs GetClients()

      AllowedScopes 属性设置的值,必须在GetApiResources()  GetApis() 里提前定义好,并且在 StartUp.cs 里已经注册

       new Client{
           ClientId="mvc client", //客户端Id
           ClientName="测试客户端", //客户端名称 随便写
           AllowedGrantTypes=GrantTypes.Code,//验证模式
           ClientSecrets=
           {
               new Secret("mvc secret".Sha256()) //客户端验证密钥
           },
           // 登陆以后 我们重定向的地址(客户端地址),
           // {客户端地址}/signin-oidc是系统默认的不用改,也可以改,这里就用默认的
           RedirectUris = { "http://localhost:5003/signin-oidc" },
           //注销重定向的url
           PostLogoutRedirectUris = { "http://localhost:5003/signout-callback-oidc" },
           //是否允许申请 Refresh Tokens
           //参考地址 https://identityserver4.readthedocs.io/en/latest/topics/refresh_tokens.html
           AllowOfflineAccess=true,
           //将用户claims 写人到IdToken,客户端可以直接访问
           AlwaysIncludeUserClaimsInIdToken=true,
           //客户端访问权限
           AllowedScopes =
           {
               "api1",
               IdentityServerConstants.StandardScopes.OpenId,
               IdentityServerConstants.StandardScopes.Email,
               IdentityServerConstants.StandardScopes.Address,
               IdentityServerConstants.StandardScopes.Phone,
               IdentityServerConstants.StandardScopes.Profile,
               IdentityServerConstants.StandardScopes.OfflineAccess
           }
       }

    5、注册相关信息(StartUp.cs)

      ConfigureServices() 注意这里我修改了路径,如果你使用的是 git 下载下来的不需要修改,我这里修改是我把 git 下载的改动了

        services.AddIdentityServer(options =>
        {
            //默认的登陆页面是/account/login
            options.UserInteraction.LoginUrl = "/login";
            //授权确认页面 默认/consent
            //options.UserInteraction.ConsentUrl = "";
        })
        .AddDeveloperSigningCredential()  
        .AddInMemoryApiResources(IdpConfig.GetApis())
        .AddInMemoryClients(IdpConfig.GetClients())
        .AddTestUsers(TestUsers.Users)
        .AddInMemoryIdentityResources(IdpConfig.GetApiResources());

      Configure()

       // 要写在 UseMvc()前面
       app.UseIdentityServer(); 
       app.UseMvcWithDefaultRoute();

    二、客户端

    1、添加认证相关信息(StartUp.cs)

      ConfigureServices() 

       //关闭了 JWT 身份信息类型映射
       //这样就允许 well-known 身份信息(比如,“sub” 和 “idp”)无干扰地流过。
       //这个身份信息类型映射的“清理”必须在调用 AddAuthentication()之前完成
       //区别可参考下面截图
       JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
       //添加认证信息
       services.AddAuthentication(options =>
       {
           options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
           options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
       })
           .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
           .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
           {
               options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               //认证服务器
               options.Authority = "http://localhost:5002";
               //去掉  https
               options.RequireHttpsMetadata = false;
               options.ClientId = "mvc client";
               options.ClientSecret = "mvc secret";
               //把 token 存储到 cookie
               options.SaveTokens = true;
               options.ResponseType = OpenIdConnectResponseType.Code;
               //添加申请 权限 ,这里的 scope 必须在授权服务端定义的客户端 AllowedScopes 里
               options.Scope.Clear();
               options.Scope.Add("api1");
               options.Scope.Add(OidcConstants.StandardScopes.OpenId);
               options.Scope.Add(OidcConstants.StandardScopes.Email);
               options.Scope.Add(OidcConstants.StandardScopes.Address);
               options.Scope.Add(OidcConstants.StandardScopes.Phone);
               options.Scope.Add(OidcConstants.StandardScopes.Profile);
               options.Scope.Add(OidcConstants.StandardScopes.OfflineAccess);
    
               options.Events = new OpenIdConnectEvents
               {
                   OnAuthenticationFailed = context =>
                   {
                       context.HandleResponse();
                       context.Response.WriteAsync("验证失败");
                       return Task.CompletedTask;
                   }
               };
           });

      Configure()

       //写在 UseMvc() 前面
       app.UseAuthentication();
       app.UseMvcWithDefaultRoute();

      JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); 区别

    2、添加测试Controller

        [Authorize]
        public class TestController : Controller
        {
            /// <summary>
            /// 测试从服务端认证
            /// </summary>
            /// <returns></returns>
            public async Task<IActionResult> Private()
            {
                var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
                var idToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.IdToken);
    
                var refreshToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
                var code = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.Code);
    
                var model = new HomeViewModel
                {
                    Infos = new Dictionary<string, string>()
                    {
                        {"AccessToken", accessToken },
                        {"IdToken", idToken },
                        {"RefreshToken", refreshToken },
                        {"Code", code } //code 是空 因为code 是一次性的
                    }
                }; 
                return View(model);
            }
    
            /// <summary>
            /// 测试请求API资源(api1)
            /// </summary>
            /// <returns></returns>
            public async Task<IActionResult> SuiBian()
            {
                var accesstoken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);
                if (string.IsNullOrEmpty(accesstoken))
                {
                    return Json(new { msg = "accesstoken 获取失败" });
                }
                var client = new HttpClient();
                client.SetBearerToken(accesstoken);
                var httpResponse = await client.GetAsync("http://localhost:5001/api/suibian");
                var result = await httpResponse.Content.ReadAsStringAsync();
                if (!httpResponse.IsSuccessStatusCode)
                {
                    return Json(new { msg = "请求 api1 失败。", error = result });
                }
                return Json(new
                {
                    msg = "成功",
                    data = JsonConvert.DeserializeObject(result)
                });
            }
        }

    三、API资源

    参考上一篇《IdentityServer4 (1) 客户端授权模式》

    修改SuiBianController.Get()

       [HttpGet]
       public IActionResult Get()
       {
           return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
       }

    四、测试

    1、端口说明

      【客户端:5003】【 API :5001 】【 授权认证服务器:5002】,并确认3个端口可以正常访问

      

    2、客户端访问

      2.1 、输入地址:http://localhost:5003/test/private 查看是否可正常跳转到授权服务器,   

      

      2.2、正常跳转,输入账号(alice)密码(alice)并登陆。登陆成功,并显示可授权的信息

      

      2.3、点击授权同意,返回相关授权信息,并展示在页面

      

      2.4 、输入地址 http://localhost:5003/test/suibian 访问 API 资源,正确返回

        

  • 相关阅读:
    数据库 Mysql事务详解
    数据库 Mysql内容补充二
    数据库 Mysql内容补充一
    优化Django ORM中的性能问题(含prefetch_related 和 select_related)
    django高级
    百度,谷歌,360,搜狗,神马等蜘蛛IP段
    中国电信、联通、移动、教育IP分布
    sed 给文件每行末尾追加相同字符
    centos7 lvs keepalived做DNS集群负载
    Notepad++ 删除空白行的方法
  • 原文地址:https://www.cnblogs.com/Zing/p/13385756.html
Copyright © 2011-2022 走看看