IdentityServer4 之Client Credentials走起來


前言

API裸奔是絕對不允許滴,之前專門針對這塊分享了jwt的解決方案(WebApi接口裸奔有風險);那如果是微服務,又怎么解決呢?每一個服務都加認證授權也可以解決問題,只是顯得認證授權這塊冗余,重復在搞事情;IT大佬肯定容忍不了,對於微服務架構,統一的認證授權中心那是必須的。

隨着.NetCore的發布,IdentityServer4隨之而出,是.Net Foundation的成員之一,專門針對.NetCore而出的認證授權框架,當前.Net圈是比較火的啦;再配上微服務認證授權的必要性,我決定以此開始入手進行微服務架構學習分享;

主要的學習分享思路為敲代碼為向導,如果遇到相關理論概念,結合代碼案例進行解釋,不在單獨針對理論知識整理相關文章(主要是擔心歸納總結不好,讓小伙伴疑惑,所以就想着結合應用案例解釋比較容易理解)。

正文

IdentityServer4 主要的功能就是認證和授權,其他功能這里先假裝不知道;主要目的就是想用其統一保護各個微服務的接口;先來理解一下認證和授權:

  • 授權(Authorization):在用戶身份認證通過之后,授予用戶訪問資源的過程或是用戶授予第三系統訪問自己資源的過程,資源可能是個人信息、文件、數據、接口等;OAuth2是現在比較火的授權標准,對於授權流程,后續會舉例說明;

    在公司,假如小伙伴是領導,在出差或休假的時候,通常會通過口頭、郵件、信息等方式將一些工作臨時委托給某人處理,比如簽字、參會等,這個過程叫做授權,如果沒有授權,簽字無效,也不能隨意參會;

  • 認證(Authentication):用戶身份認證,可以將其理解為登錄;系統驗證身份憑據是否合法,比如用戶名/密碼、人臉識別等方式;OpenId Connect是目前比較流行的身份認證標准協議,OpenID是一個去中心化的網上身份認證系統,OpenID Connect是在OAuth2基礎進行擴展,增加身份認證和相關身份標識信息;

    稍微有點規模的公司,通常都有自己的辦公樓,有專門的保安人員,管控非公司人員的進入, 如果是公司人員,刷卡識別即可進入,如果是非公司人員需要登記個人信息確認才能進入,這個過程可以理解為身份認證;只有驗證信息之后才能進入公司。

IdentityServer4 已經將OpenID Connect和OAuth 2.0封裝實現,開發者開箱即用,無需再重新自己實現細節,但如果有需要,小伙伴可以在IdentitySever4基礎進行擴展個性化需求;

在授權過程中,根據應用場景不同,有四種授權模式可以選擇,如下:

  1. Authorization Code(授權碼):最完整的授權模式,也是相對比較安全的模式,適用於有后台的應用程序,如MVC項目;
  2. Implicit(簡化模式):簡化授權碼模式,適用於無后台的應用程序,如前后端分離項目;
  3. Resource Owner Password Credentials(資源所有者密碼):直接通過用戶名和密碼獲得授權,這種適用於高度信任的應用,因為需要輸入用戶名和密碼,安全泄露風險高;
  4. **Client Credentials(客戶端模式) **:這是無用戶操作模式,適用於機器對機器的對接,沒有用戶干預的應用,如后台任務調度應用,采集數據應用等;
  5. 混合模式:以上四種的組合。

其他理論先不說了,我們邊擼碼邊聊,這樣記憶深刻一點,這里就從最簡單的Client Credentials開始:

Client Credentials 客戶端授權模式

客戶端模式沒有用戶,就只是單純的機器對機器的交互,大概的流程如下:

image-20201230210937580

流程簡要說明:

  1. 首先客戶端帶上憑據向授權服務器獲取AccessToken,這里的客戶端憑據是提前在授權服務器上備案過的;
  2. 授權服務器驗證客戶端憑據,成功之后直接返回AccessToken;
  3. 客戶端在帶上AccessToken訪問資源服務器;
  4. 資源服務器正常返回結果,如果沒有AccessToken是不能訪問受保護資源的;

來,結合流程看看代碼怎么實現,一步一步來:

>>>先創建API項目---資源服務器
  1. 創建一個OrderController,並在里面新增一個Orders 接口,接口沒有進行保護;

    image-20201231105706789

  2. 接口沒有進行保護,可以任意訪問,如下:

    image-20201231105517893

>>>再創建認證授權中心項目---授權服務器,將資源服務保護起來

以上的API接口裸奔是有風險的,現在需要統一的認證授權中心進行保護,如下:

  1. 新創建一個API項目,並引入IdentityServer4包,並在內存中模擬相關數據,方便測試;

    image-20201231123450257

    術語解釋

    ApiScope:就是一個作用域范圍,生成的Token只能訪問指定范圍的資源;

    Client:這里的客戶端就是應用,比如MVC項目、純前端項目、Winfrom/WPF、APP等,必須首先在授權服務器中進行備案並獲得授權服務器分配的標識和密碼,后續用於獲取AccessToken;

  2. 模擬數據准備好了,就在Startup中進行對應的注入和配置,並開啟中間件,如下:

    image-20201231124703423

  3. 這樣就初步完成授權服務器的搭建,這里監聽的端口改為6100了,用Postman先來測測是否能正常獲取Token,如下:

    image-20201231133328069

    可能有新手小伙伴會問,咋知道是這個地址能獲取token的? 小伙伴可以在瀏覽器中輸入以下鏈接,即可看見授權服務器的相關信息(授權服務器地址+/.well-known/openid-configuration):

    image-20201231133558917

  4. 授權服務器已經好了,准備將資源服務器接入到授權服務器,對API接口進行保護(ApiDemo項目中),如下:

    image-20201231135258124

    注:ApiDemo項目中需要Microsoft.AspNetCore.Authentication.JwtBearer包,因為項目是基於.NetCore3.1的,所以這里引用的包版本為3.1.10。

  5. 然后在接口上面加上[Authorize]特性,將接口保護起來,看運行效果如下:

    image-20201231135953125

  6. 在Postman中測試,先獲取AccessToken,然后將獲取的AccessToken加入到Header中請求資源服務器中受保護的API,如下:

    image-20201231140952036

>>>真實客戶端訪問受保護API---控制台

建一個控制台項目,具體步驟如圖:

image-20201231144413626

這里就不用文字說明步驟,小伙伴一邊看代碼,一邊看注釋,這樣應該比較清晰點:

static async Task Main(string[] args)
{
    // 1. 創建一個HttpClient用於請求
    var client = new HttpClient();
    // 2. 獲取授權服務器的相關信息,IdentityModel已經將其封裝好了
    var disco = await client.GetDiscoveryDocumentAsync("http://localhost:6100");
    // 3. 檢查是否請求錯誤
    if (disco.IsError)
    {
        // 錯誤就打印錯誤信息,然后直接返回
        Console.WriteLine(disco.Error);
        return;
    }
    // 4. 通過授權服務分配的標識,向授權服務器請求AccessToken
    var tokenResp = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
    {
        // 指定獲取token的地址,IdentityModel進行封裝,直接使用即可
        Address = disco.TokenEndpoint,
        // 指定授權服務器分配的客戶端標識
        ClientId = "client",
        // 指定授權服務器分的客戶端密碼
        ClientSecret = "ordersecret"
    });
    // 5. 檢查獲取Token是否成功
    if (tokenResp.IsError)
    {
        // 如果失敗,打印錯誤消息並返回
        Console.WriteLine(tokenResp.Error);
        return;
    }

    // 6. 創建一個請求API資源的HttpClient
    var apiClient = new HttpClient();
    // 7. 將獲取到的Token以Bearer的方案設置在請求頭中
    apiClient.SetBearerToken(tokenResp.AccessToken);
    // 8. 向資源服務器中請求受保護的API
    var contentResp = await apiClient.GetAsync("http://localhost:5000/api/Order");
    // 9. 打印對應的消息
    if (contentResp.IsSuccessStatusCode)
    {
        var content = await contentResp.Content.ReadAsStringAsync();
        Console.WriteLine(JArray.Parse(content));
    }
    else
    {
        Console.WriteLine(contentResp.StatusCode);
    }

    Console.ReadLine();
}

到這里離完成還差一步了,什么,資源不是保護了嗎,受保護資源也能正常訪問了,還差哪一步?

在授權服務器模擬備案客戶端的時候,是不是指定了訪問資源的作用域,也就是說,備案過的客戶端只能訪問被授權的API資源,而現在拿到的AccessToken都能訪問資源服務器中所有受保護的資源,那是因為資源服務器中的API資源沒有限制作用域訪問,而在實際項目中,並不是拿到AccessToken就能隨便訪問,需要做限制,繼續往下看↓↓↓

image-20201231152850521

假如指定的scope值和客戶端在授權服務器中備案時設置的不一樣,就算獲取到AccessToken也不能正常訪問資源,會報403錯誤,這里我不截圖,小伙伴下去試試。

可能小伙伴會比較急,這都是啥玩意,全是硬編碼,垃圾文; 別別別,說好的學習分享嘛,一步一個腳印來嘛,最終肯定是小伙伴想要的,也是我學習的目標;

關於客戶端憑據生成的Token,在jwt.io網站解析看看,記錄一下,看看后面有用戶參與的情況,生成的Token解析出來會有什么不同呢,先上個圖(圖中解析出來的屬性之前在WebApi接口裸奔有風險有說過):

image-20201231154747613

總結

從這篇開始,后續會盡快更新學習分享,小伙伴們加入一起學習,一起討論。下一篇說說Resource Owner Password Credentials.

一個被程序搞丑的帥小伙,關注"Code綜藝圈",跟我一起學~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM