IdentityServer4筆記整理(更新中)

博客與筆記沒法實時同步, 筆記最新連接

OAuth 2.0

定義:OAuth 2.0是一個開放受權標準:容許資源全部者(用戶)受權第三方應用訪問該用戶在某服務上的特定私有資源,但不提供帳號密碼給第三方應用。
安全提示:受權碼和全部令牌必須經過Tsl加密傳輸。進入OAuth 2.0官網前端

OAuth 2.0協議流程圖

     +--------+                               +---------------+
     |        |--(A)- Authorization Request ->|   Resource    |
     |        |                               |     Owner     |
     |        |<-(B)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(C)-- Authorization Grant -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+
                         (OAuth 2.0協議流程)
                         
    (A)客戶端發起受權請求,客戶端能夠向資源全部者直接發起受權請求,建議的作法是將受權服務器做爲中間人,客戶端向受權服務器發起受權請求
    (B)客戶端收到受權憑證(和具體的受權類型有關)
    (C)客戶端經過受權憑證向受權服務器請求訪問令牌
    (D)受權服務器驗證受權憑證和客戶端,若是經過驗證,返回給客戶端訪問令牌
    (E)客戶端經過訪問令牌向資源服務器發出訪問請求
    (F)資源服務器驗證訪問令牌有效後,返回請求的資源

受權碼模式

受權碼模式:code的生命週期必須短暫、或者只能單次使用,若是code被屢次使用,受權服務器必須使該code生成的全部令牌失效。訪問令牌由client後端保存。web

     +----------+
     | Resource |
     |   Owner  |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier      +---------------+
     |         -+----(A)-- & Redirection URI ---->|               |
     |  User-   |                                 | Authorization |
     |  Agent  -+----(B)-- User authenticates --->|     Server    |
     |          |                                 |               |
     |         -+----(C)-- Authorization Code ---<|               |
     +-|----|---+                                 +---------------+
       |    |                                         ^      v
      (A)  (C)                                        |      |
       |    |                                         |      |
       ^    v                                         |      |
     +---------+                                      |      |
     |         |>---(D)-- Authorization Code ---------'      |
     |  Client |          & Redirection URI                  |
     |         |                                             |
     |         |<---(E)----- Access Token -------------------'
     +---------+       (w/ Optional Refresh Token)
     
    (A)客戶端引導用戶代理(瀏覽器)到達受權終結點,並攜帶參數:response_type(code)、client_id、redirect_uri、scope、state
    (B)受權服務器經過用戶代理(瀏覽器)驗證資源全部者,並肯定資源全部者是否給予客戶端受權
    (C)驗證資源全部者成功,而且容許受權,受權服務器將用戶代理(瀏覽器)重定向到redirect_uri,並返回code和受權請求的state
    (D)客戶端向受權服務器令牌終結點請求令牌,攜帶參數:grant_type、code、redirect_uri、client_id、secret(可選)
    (E)受權服務器驗證code、client_id、secret是否有效,並驗證redirect_uri是否與步驟C中一致,若是驗證成功,攜帶Access Token將用戶代理(瀏覽器)重定向到redirect_uri

簡化模式

要點:不支持刷新令牌,訪問令牌編碼在Url中,因此會有暴露的風險(在Oauth2.0官網中已經不推薦使用)算法

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          ^
          |
         (B)
     +----|-----+          Client Identifier     +---------------+
     |         -+----(A)-- & Redirection URI --->|               |
     |  User-   |                                | Authorization |
     |  Agent  -|----(B)-- User authenticates -->|     Server    |
     |          |                                |               |
     |          |<---(C)--- Redirection URI ----<|               |
     |          |          with Access Token     +---------------+
     |          |            in Fragment
     |          |                                +---------------+
     |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
     |          |          without Fragment      |     Client    |
     |          |                                |    Resource   |
     |     (F)  |<---(E)------- Script ---------<|               |
     |          |                                +---------------+
     +-|--------+
       |    |
      (A)  (G) Access Token
       |    |
       ^    v
     +---------+
     |         |
     |  Client |
     |         |
     +---------+
    (A)客戶端引導用戶代理(瀏覽器)到達受權終結點,並攜帶參數:response_type(token)、client_id、redirect_uri、scope、state
    (B)受權服務器經過用戶代理(瀏覽器)驗證資源全部者,並肯定資源全部者是否給予客戶端受權
    (C)驗證資源全部者成功,而且容許受權,受權服務器將用戶代理(瀏覽器)重定向到redirect_uri,redirect_uri的URL 錨點(fragment)部分包括了響應參數(以#hash值的方式追加):access_token、token_type、expires_in、scope、state
    (D)用戶代理(瀏覽器)向redirect_uri發出請求,但不包括URL 錨點,用戶代理在本地保存了fragment。
    (E)redirect_uri的服務器返回頁面給瀏覽器,該頁面須要包含解碼fragment的腳本。
    (F)瀏覽器接收到頁面後,執行腳本,將C中的響應參數解碼出來
    (G)用戶代理(瀏覽器)將響應參數傳遞給客戶端

資源全部者密碼模式

用戶將用戶名及密碼提供給可信任的第三方應用,第三方應用向令牌終結點請求令牌。經常使用於第一方應用進行登錄. 優勢:應用不須要存儲用戶帳號、密碼,經過刷新令牌保持持久登錄,下降密碼泄露的風險。json

     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          v
          |    Resource Owner
         (A) Password Credentials
          |
          v
     +---------+                                  +---------------+
     |         |>--(B)---- Resource Owner ------->|               |
     |         |         Password Credentials     | Authorization |
     | Client  |                                  |     Server    |
     |         |<--(C)---- Access Token ---------<|               |
     |         |    (w/ Optional Refresh Token)   |               |
     +---------+                                  +---------------+

            Figure 5: Resource Owner Password Credentials Flow

   (A)  資源全部者向客戶端提供其用戶名及密碼
   (B)  客戶端攜帶用戶名、密碼向受權服務器的令牌終結點發起請求
   (C)  受權服務器受權客戶端,並驗證用戶名、密碼是否有效。若是有效,返回訪問令牌
#請求token
Request:
    POST /connect/token HTTP/1.1
    Host: idsrv-server.com
    Content-type: application/x-www-form-urlencoded
    body:
    {
        grant_type:password
        username:dd
        password:dd
        client_id:eshopOnVue
        scope:orders(可選參數)
    }

#請求刷新令牌:原刷新令牌失效、以前頒發的access_token不受影響(須要實現手動失效)
Request:
    POST /connect/token HTTP/1.1
    Host: idsrv-server.com
    Content-type: application/x-www-form-urlencoded
    body:
    {
        grant_type:refresh_token
        refresh_token:e4364377ec69c8d5c06a49d7b74efbd2a29015ac37e9ede8e17597d348931d32
        client_id:eshopOnVue
    }
Respose:
{
    "id_token": "eyJhbGciO.iJSUzI1NiI.sImtpZCw",
    "access_token": "eyJhb.GciOiJSUz.I1NiIsIm",
    "expires_in": 3600,
    "token_type": "Bearer",
    "refresh_token": "60e7dda6e30473ce6dc0a1656b38c174a74ef73310d"
}
#經過access_token請求用戶終結點(須要scope:profile):/connect/userinfo

客戶端憑證模式

客戶端直接使用自身的憑證向受權服務器終結點請求訪問令牌。只能用於可信的客戶端。不支持刷新令牌、沒法訪問用戶資源scope(openid、profile、email等)後端

     +---------+                                  +---------------+
     |         |                                  |               |
     |         |>--(A)- Client Authentication --->| Authorization |
     | Client  |                                  |     Server    |
     |         |<--(B)---- Access Token ---------<|               |
     |         |                                  |               |
     +---------+                                  +---------------+

   (A)  客戶端向受權服務器令牌終結點請求訪問令牌
   (B)  受權服務器對客戶端進行認證,若是成功,返回訪問令牌

Request:
    POST /connect/token HTTP/1.1  #請求方式只能爲post
    Host: idsrv-server.com
    Content-type: application/x-www-form-urlencoded #參數只能放在body裏面
    body:
    {
        grant_type:client_credentials
        client_id:ClientCredentials
        client_secret:iwiaXNzIjoibnVsbCIsImF1ZCI6WyJudWxsL3Jlc291cmNlcyIsIm9yZGVycyJdLCJjbGllbnRfaWQiOiJDb
        scope:orders openid(可選,默認請求全部scope)
    }
Response:
    HTTP/1.1 200 OK
    Content-Type: application/json
    Cache-Control: no-store
    Pragma: no-cache
     
    {
      "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
      "token_type":"bearer",
      "expires_in":3600
    }

OpenID Connect(OIDC)

定義:它在OAuth2上構建了一個身份層,是一個基於OAuth2協議的身份認證標準協議瀏覽器

image

OIDC協議流程圖

+--------+                                   +--------+
|        |                                   |        |
|        |---------(1) AuthN Request-------->|        |
|        |                                   |        |
|        |  +--------+                       |        |
|        |  |        |                       |        |
|        |  |  End-  |<--(2) AuthN & AuthZ-->|        |
|        |  |  User  |                       |        |
|   RP   |  |        |                       |   OP   |
|        |  +--------+                       |        |
|        |                                   |        |
|        |<--------(3) AuthN Response--------|        |
|        |                                   |        |
|        |---------(4) UserInfo Request----->|        |
|        |                                   |        |
|        |<--------(5) UserInfo Response-----|        |
|        |                                   |        |
+--------+                                   +--------+
EU:End User:一我的類用戶。
RP:Relying Party ,用來代指OAuth2中的受信任的客戶端,身份認證和受權信息的消費方;
OP:OpenID Provider,有能力提供EU認證的服務(好比OAuth2中的受權服務),用來爲RP提供EU的身份認證信息;
ID Token:JWT格式的數據,包含EU身份認證的信息。
UserInfo Endpoint:用戶信息接口(受OAuth2保護),當RP使用Access Token訪問時,返回受權用戶的信息,此接口必須使用HTTPS。
    (1).RP發送一個認證請求給OP;
    (2).OP對EU進行身份認證,而後提供受權;
    (3).OP把ID Token和Access Token(須要的話)返回給RP;
    (4).RP使用Access Token發送一個請求到UserInfo EndPoint;
    (5).UserInfo EndPoint返回EU的Claims。

OIDC在OAuth之上的擴展

  • [ ] Scope:openid(用來區分這是一個OIDC的Authentication請求,而不是OAuth2的Authorization請求)
  • [ ] response_type:id_token,在implicit模式下請求id_token
  • [ ] id_token:身份令牌,是一個受權服務器提供的包含用戶信息(由一組Cliams構成以及其餘輔助的Cliams)的JWT格式的數據結構
  • [ ] UserInfo Endpoint:經過id_token從用戶信息終結點得到一組EU相關的Claims,好比能夠將Claims從token移除,避免token過大,只保留sub,經過UserInfo Endpoint查詢Claims
"response_type"參數值 OIDC受權類型
code Authorization Code Flow
id_token token Implicit Flow
code id_token Hybrid Flow
  • Authorization Code Flow:從受權終結點返回code,從令牌終結點返回token
  • Implicit Flow:從受權終結點返回全部token
  • Hybrid Flow:id_token返回給前端,access_token在後端保存(access_token的安全性要求比id_token)高

JSON Web Token

定義:JWT是一個定義一種緊湊的,自包含的而且提供防篡改機制的傳遞數據的方式的標準協議安全

JWT格式組成

JWT由3部分構成:header.payload.signature
在IdSrv中,payload中的鍵值對根據token類型和受權流程的不一樣有所區別
header:
{
  "alg": "RS256",//簽名算法
  "kid": "9dcf733a1192a6da053e64c6ee22ff87",
  "typ": "JWT"//token類型
}
payload://須要傳遞的數據
{
  "nbf": 1556591630,//該jwt在此以前無效
  "exp": 1556595230,//該jwt在此以後無效
  "iss": "http://localhost:7102",//jwt頒發者
  "iat": 1516239022,//jwt頒發時間
  "aud": "http://localhost:7102/resources",//jwt接收者
  "client_id": "jsImplicit",
  "sub": "SubjectId",//用戶惟一id
  "auth_time": 1556591629,//受權時間
  "idp": "local",//identityProvider
  "name": "Username",
  "scope": [
    "openid",
    "profile"
  ],
  "amr": [
    "pwd"//authenticationMethod
  ]
}
signature://token生成方使用私匙生成token,token消費方使用用公匙驗證token是否被修改過
RSASHA256(
  base64UrlEncode(header) + "." +base64UrlEncode(payload),Public Key,Private Key
  )
#RSA簽名過程
#1.將消息內容進行base64編碼:base64UrlEncode(header) + "." +base64UrlEncode(payload)
#2.對編碼後的內容進行SHA256哈希計算(不可逆)
#3.對hash值使用privateKey加密,加密後內容做爲簽名
#4.token接收方經過publicKey對簽名進行解密獲得hash值,並將內容進行hash計算,比較兩個hash值,確保消息未被篡改
expires_in="exp"-"nbf"

RSA非對稱加密算法

1 RSA算法特色:
1.1 公鑰私鑰對等,能夠互換
1.2 私鑰須要保存在可靠方
1.3 沒法從公鑰計算出私鑰(理論上從私鑰也沒法計算出公鑰,例外:openssl的公鑰e固定爲65537,且私鑰文件中含有額外的參數,能夠從私鑰計算出公鑰)
2 RSA應用
2.1 加密:消息發送方:公鑰加密=>消息接收方:私鑰解密(用於加密傳遞消息) 
##缺點:沒法防止僞造消息
2.2 簽名:消息發送方:私鑰加密=>消息接收方:公鑰解密(用於消息簽名,防止消息被篡改)
##缺點:消息明文傳輸

HTTPS簡單流程

1.服務器經過非對稱算法生成私鑰公鑰
2.服務方將公鑰提供給相關機構(CA)
3.CA生成證書並頒發給服務器:
{
    證書發佈機構CA
    證書有效期
    公鑰
    證書全部者
    簽名(簽名方法與JWT相似,私鑰屬於CA)
    ...
}
4.服務器將證書發送給客戶端:
{
    1.TCP三次握手
    2.創建tunnel
    3.client hello(包括SessionId,能夠避免從新握手,並從新使用已有對話密鑰)
    4.server hello
    5.發送Certificate給客戶端
}
5.客戶端經過系統中內置的CA公鑰驗證證書的合法性
6.客戶端經過服務方公鑰與服務器協商對稱加密算法與密鑰
7.進行對稱加密通訊

參考:https://zhuanlan.zhihu.com/p/22142170 https://zhuanlan.zhihu.com/p/27395037服務器

IdentityServer4

受權碼模式

PKCE(Proof Key for Code Exchange)

利用不可逆算法,確保在被竊取了受權碼或其餘密匙的狀況下,也沒法向受權服務器換取訪問令牌session

PKCE流程:
1.客戶端隨機生成一串字符:
{
    code_verifier=base64url(RandomString),
    code_challenge=base64url(sha256(code_verifier)) 
}
2.客戶端攜帶code_challenge向受權服務器發起受權請求
3.受權服務器對客戶端進行認證成功後返回受權碼,並保存code_challenge
4.客戶端獲取到受權碼以後,攜帶code_verifier向令牌終結點發起請求,換取Access Token
5.受權服務器驗證受權碼,將code_verifier進行sha256計算並url編碼後與code_challenge對比,若是一致,頒發訪問令牌

參考:https://tonyxu.io/zh/posts/2018/oauth2-pkce-flow/數據結構

受權碼模式流程

#Step 1 客戶端向受權服務器受權終結點發起請求:
    GET /connect/authorize HTTP/1.1
    Host: Idsrv.com,
    Query String Parameters:
    {
        client_id: Swagger_UI
        redirect_uri: http://localhost:9528/Callback
        response_type: code
        scope: openid profile orders
        state: 668ae852a74f4923ad140d79d2f10fee
        code_challenge: i2CnOeIHTBZZrAsgzEZV3-KpMTb_OCvl05ydETjrqIc
        code_challenge_method: S256
    }
state:客戶端隨機生成的字符串,受權服務器在重定向到redirect_uri時會原樣返回,客戶端檢查state是否相同,來防止CSRFF攻擊
#step 2 受權服務器對客戶端進行認證,認證成功,受權服務器返回302重定向並攜帶ReturnUrl參數:
Response:
{
    Status Code: 302 Found,
    Location: http://localhost:6102/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3DSwagger_UI%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A9528%252FCallback%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520orders%26state%3D668ae852a74f4923ad140d79d2f10fee%26code_challenge%3Di2CnOeIHTBZZrAsgzEZV3-KpMTb_OCvl05ydETjrqIc%26code_challenge_method%3DS256
}
#step 3 /Account/Login請求返回登錄頁面,並將ReturnUrl寫入登錄頁面
#step 4 用戶在登錄頁面發起登錄請求:
參數:
{
    1.用戶名
    2.密碼
    3.ReturnUrl
}
#step 4 受權服務器驗證用戶名、密碼成功,且ReturnUrl有效:
{
    1.經過HttpContext.SignInAsync爲當前請求上下文頒發登錄憑證:
    Set-Cookie:
    {
        idsrv.session:'8c9e9e80f92da2551c77dc6ab03c69ca,path=/'
        idsrv:'CfDJ8IOXoUULE4dDgZ02v48m533Xg,expires=Mon,08 Jul 2019 08:38:26 GMT, path=/,httponly'
    }
    2.發起重定向到ReturnUrl
}
#step 5 在 /connect/authorize/callback 終結點:
{
    受權服務器更新Cookie:idsrv,並重定向到redirect_uri,攜帶如下參數:
    {
        code: 21a9deaac4457e29f669a91bb36795c048aae5680b49ae3a1ffafa50aff0d169
        scope: openid profile orders
        state: 668ae852a74f4923ad140d79d2f10fee
        session_state: ZcDaWfAmzNGLsXS3-1ofTvNGryU-KxeurTpPLxP6oF0.89cd4e2083165f0701dbd2181ede5b7b(會話狀態)
    }
}
#step 6 在redirect_uri向令牌終結點請求訪問令牌:
POST /connect/token HTTP/1.1
Host: Idsrv.com,
Content-type: application/x-www-form-urlencoded
body:
{
    client_id: Swagger_UI
    code: 21a9deaac4457e29f669a91bb36795c048aae5680b49ae3a1ffafa50aff0d169
    redirect_uri: http://localhost:9528/Callback
    code_verifier: dde8c25afc8d42728225154fea0aa098556671d3344c423f9007f1895b47dbf2be2116c7c5f34720842383ec9a7e0a66
    grant_type: authorization_code
}
#step 7 受權服務器驗證code和code_verifier,並頒發訪問令牌

刷新訪問令牌

配置new Oidc.UserManager()時 設置scope=offline_access,直接經過刷新令牌請求訪問令牌。offline_access優先於silent_redirect_uri

經過Iframe頁面進行靜默刷新:
#step 1 經過Iframe頁面向受權終結點發起請求:
    GET /connect/authorize HTTP/1.1
    Host: Idsrv.com,
    Cookie: 
    {
        idsrv.session=8c9e9e80f92da2551c77dc6ab03c69ca;
        idsrv=CfDJ8IOXoUULE4dDgZ02v48m53JzoKhJcuEwwXcdvHRYodIZ2nTuD
    }
    Query String Parameters:
    {
        client_id: Swagger_UI
        redirect_uri: http://localhost:9528/SilentCallback
        response_type: code
        scope: openid profile orders
        state: 54207b37bb644f90800d0993d5a5c210
        code_challenge: 8bOHunvRggEM9m3Hwb-8m24KIiRV9rPSbz0OOvTP7D0
        code_challenge_method: S256
        prompt: none
        id_token_hint: eyJhbGciOiJSUzI1NiIsImtp2UFyLw
    }
id_token_hint://爲以前獲取的id_token
prompt:none//用來指示受權服務器是否引導用戶從新認證和贊成受權 
#step 2 重定向到redirect_uri,攜帶如下參數: 
{
    code: a956650cd653debe11989d225c2caa2619521b9176b444fbcc83d3a1663bf1ed
    scope: openid profile orders
    state: 54207b37bb644f90800d0993d5a5c210
    session_state: SGmLH8gIy6VAPtdlT6_zQtix_VM229bPkpY0OpwQ6fc.229a9d1e0be856145c035db0aa6033cc
}
#step 3 在redirect_uri頁面執行new Oidc.UserManager().signinSilentCallback()
#step 4 向令牌終結點請求訪問令牌
登出操做:
Js客戶端mgr.signoutRedirect()
1.向IdSrv請求/connect/endsession 並攜帶如下兩個參數
id_token_hint:
post_logout_redirect_uri: 登出回調地址
2.重定向到/Account/Logout 並攜帶生成的logoutId參數
3.在Logout裏清除await HttpContext.SignOutAsync() 並跳轉到post_logout_redirect_uri

資源全部者密碼模式

相關文章
相關標籤/搜索