IdentityServer4 (3) 授权码模式(Authorization Code)

  • A+
所属分类:.NET技术
摘要

  git地址:https://github.com/yizhaoxian/CoreIdentityServer4Demo.git  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)》


写在前面

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

4、流程图

IdentityServer4 (3) 授权码模式(Authorization Code)

  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,解压后如下图

  IdentityServer4 (3) 授权码模式(Authorization Code)

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(); 区别

IdentityServer4 (3) 授权码模式(Authorization Code)

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个端口可以正常访问

  IdentityServer4 (3) 授权码模式(Authorization Code)

2、客户端访问

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

  IdentityServer4 (3) 授权码模式(Authorization Code)

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

  IdentityServer4 (3) 授权码模式(Authorization Code)

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

  IdentityServer4 (3) 授权码模式(Authorization Code)

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

  IdentityServer4 (3) 授权码模式(Authorization Code)