2020年8月4日星期二

IdentityServer4 授权码模式(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 中文文档 />   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、流程图

  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 = { "" },  //注销重定向的url  PostLogoutRedirectUris = { "" },  //是否允许申请 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 = "";   //去掉 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("");   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 、输入地址 查看是否可正常跳转到授权服务器,   

  

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

  

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

  

  2.4 、输入地址 访问 API 资源,正确返回

    

IdentityServer4 授权码模式(Authorization Code)ebay账号风险控制与物流2017开年沙龙 涵哥&招商经理喊你一起聊亚马逊平台运营一二三试问:一条差评竟达800元,亚马逊不刷单在中国真做不下去吗?亚马逊运营常见问题QA集锦(564-573)亚马逊账号二审申诉模板白云山蹦极多少钱?去长隆香江野生动物园要注意什么?暑假游泳如果不小心溺水怎么办?溺水如何自救?

没有评论:

发表评论