IdentityServer4 之Client Credentials走起來

前言

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

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

主要的學習分享思路爲敲代碼爲嚮導,若是遇到相關理論概念,結合代碼案例進行解釋,不在單獨針對理論知識整理相關文章(主要是擔憂概括總結很差,讓小夥伴疑惑,因此就想着結合應用案例解釋比較容易理解)。後端

正文

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

  • 受權(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綜藝圈",跟我一塊兒學~

相關文章
相關標籤/搜索