IdentityServer4 (3) 受權碼模式(Authorization Code)

寫在前面

一、源碼(.Net Core 2.2)

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

二、相關章節

  2.一、《IdentityServer4 (1) 客戶端受權模式(Client Credentials)
  2.二、《IdentityServer4 (2) 密碼受權(Resource Owner Password)
  2.三、《IdentityServer4 (3) 受權碼模式(Authorization Code)
  2.四、《IdentityServer4 (4) 靜默刷新(Implicit)》
  2.五、《IdentityServer4 (5) 混合模式(Hybrid)》git

三、參考資料

  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 github

四、流程圖

  一、訪問客戶端受保護的url
  二、跳轉到受權服務器認證
  三、用戶填寫認證(帳號密碼)
  四、認證成功,選擇受權的scope
  五、受權成功(點擊贊成受權),攜帶受權碼code返回客戶端
  六、客戶端經過後臺通訊請求AccessToken 和IdToken,若是設置了 OfflineAccess=true也會返回RefreshToken(能夠刷新AccessToken)web

  整個訪問流程建議使用fiddler 抓包查看,更好的瞭解api

1、服務端

一、下載一個官方示例

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

  根據本身使用的Core 版本下載對應的,點擊 tags 裏找,我下載的是2.5,解壓後以下圖cookie

  

二、新建一個 web 項目 .NetCore 2.2版本

   把官網下載的文件添加到項目中,Quickstart 我換成了Controllersmvc

三、添加配置類 (IdpConfig.cs)

  參看 《IdentityServer4 (1) 客戶端受權模式(Client Credentials)》裏的,直接複製過來就能夠了app

四、添加客戶端

  IdpConfig.cs GetClients()async

  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
       }
   }

五、註冊相關信息(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();

2、客戶端

一、添加認證相關信息(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(); 區別

二、添加測試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)
            });
        }
    }

3、API資源

參考上一篇《IdentityServer4 (1) 客戶端受權模式》

修改SuiBianController.Get()

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

4、測試

一、端口說明

  【客戶端:5003】【 API :5001 】【 受權認證服務器:5002】,並確認3個端口能夠正常訪問

  

二、客戶端訪問

  2.1 、輸入地址:http://localhost:5003/test/private 查看是否可正常跳轉到受權服務器,   

  

  2.二、正常跳轉,輸入帳號(alice)密碼(alice)並登錄。登錄成功,並顯示可受權的信息

  

  2.三、點擊受權贊成,返回相關受權信息,並展現在頁面

  

  2.4 、輸入地址 http://localhost:5003/test/suibian 訪問 API 資源,正確返回

    

相關文章
相關標籤/搜索