【One by One系列】IdentityServer4(六)授權碼流程原理之SPA


【One by One系列】IdentityServer4(四)授權碼流程中提過一句:

為了安全,IdentityServer4是帶有PKCE支持的授權碼模式

我們來回顧一下授權碼流程

(A)用戶訪問客戶端,后者將前者導向認證服務器。

(B)用戶選擇是否給予客戶端授權。

(C)假設用戶給予授權,認證服務器將用戶導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個授權碼。

(D)客戶端收到授權碼,附上早先的"重定向URI",向認證服務器申請令牌。這一步是在客戶端的后台的服務器上完成的,對用戶不可見。

(E)認證服務器核對了授權碼和重定向URI,確認無誤后,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)。

--摘自阮一峰老師-理解OAuth 2.0,自認為阮老師這塊已經寫比較清晰了,正所謂”眼前有景道不得,崔顥題詩在上頭“。

1.什么是PKCE

PKCE,全稱Proof Key for Code Exchange,上篇講到SPA,這是一種沒有后端服務器的原生客戶端,代碼都在用戶本地設備上運行,比如SPA在用戶瀏覽器上運行,Win/Mac客戶端,iOS/Android APP,如果讓這些原生客戶端安全地存放密鑰(client secret)並不現實,且容易被破解。

  • Implicit Flow:我們沒有介紹Implicit Flow,官方最新文檔也沒有,現在來看,可能就是因為安全的原因。Access Token會直接被傳遞給Redirect URL,容易被截取Access-Token
  • Authorization Code Flow:Redirect URL只會接收一個授權碼,且授權碼必須要和Client ID,Client Secret一同使用才能獲取Access Token。然而原生客戶端無法安全保存Client Secret,第三方惡意應用可以破解Client Secret,並按上述方法截取Authorization Code,同樣不建議使用。

PKCE,旨在提高移動設備上授權代碼流程執行過程中的安全性。有關該功能的定義,參閱RFC7636,微軟翻譯為保護授權碼授權。實質是通過密碼學技術手段,確保惡意第三方即使截獲到授權碼(Authorization Code)或者其他密鑰,也無法向認證服務器交換獲取Access Token。

PKCE要求所有客戶端必須需要實現的內容:

  • 隨機生成一串字符串,並用URL-Safe的Base64編碼處理,結果為:code_verifier
  • code_verifier通過SHA256哈希加密,並用URL-Safe的Base64編碼處理,結果為:code_challenge
//1.生成code_verifier
let code_verifier=generateRandomString(32);

//2.生成code_challenge
let code_challenge=generateCodeChallenge(code_verifier);

function generateRandomString(length) {
    let text = "";
    let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    for (let i = 0; i < length; i++) {
        text += possible.charAt(Math.floor(Math.random() * possible.length));
    }
    return text;
}
function generateCodeChallenge(code_verifier) {
    return code_challenge = base64URL(CryptoJS.SHA256(code_verifier))
}

2.PKCE授權碼流程

那么PKCE支持的授權碼流程就發生了變化,具體流程如下:

  • (A)客戶端除了response_type,Scope等標准參數,還必須帶上,code_challengecode_challenge_method,發起對服務器的/authorize端點請求
  • (B)服務器/authorize除了執行標准的OAuth請求驗證,還會檢查code_challengecode_challenge_method是否存在,並存儲
  • (C)服務器返回Authorization Code
  • (D)客戶端拿到Authorization Code,就用Authorization Codecode_verifier向服務端的/token端點發起請求,獲取Access-token
  • (E)服務端/token端點,處理執行標准的OAuth其你去驗證外,還會使用客戶端傳過來的code_verifier和服務端存儲的code_challenge_method來生成自己的code_challenge
  • (F)服務端會將生成code_challenge與初始請求/authorize端點的code_challenge(並根據Authorization Code存儲在服務端)兩相比較
  • 匹配,頒發access-token
    • 不匹配,拒絕該請求

流程圖

注意點:

  • 上圖的t(code_verifier)指的就是code_challenge
  • code_verifier是一個加密字符串,其所具有的熵必須要足夠大,使攻擊者無法預測或猜到其值

任何截獲到的中間方,並不能由code_challenge破解出code_verifier,這里只有客戶端知道這兩個值。所以截獲到code_challengeAuthorization Code,也換不來想要的token,換來的只有拒絕。總而言之,這樣降低惡意使用Authorization Code與Access-token的行為的風險。

3.查看IdentityServer4授權碼流程

知曉了PKCE的男人,現在想對IdentityServer4授權碼流程有一個更詳細了了解,以及對PKCE的驗證,我們使用WireShark對整個請求進行抓包。

由於fiddler智能抓包一些瀏覽器請求,對於整個過程中的一些后台請求,並不能捕獲,所以我們選擇WireShark這個工具。也不能通過瀏覽器開發者模式,由於授權重定向的過程太快,好多請求都看不清。

3.1 運行IdentityServer

cd .\IdentityServer\
dotnet run

3.2 運行wireshark

打開軟件,選擇-adapter for loopback traffic capture開始抓包

3.2 運行JavaScript客戶端

cd .\JavaScript\
dotnet run

3.2 操作

按照上一篇文章操作,登錄,注銷

3.3 停止wireshark的捕獲

停止捕獲,通過自帶的列表過濾報文,如下圖

這就是整個授權登錄,然后注銷登錄過程中,發生的http請求。

4.詳解IdentityServer4授權碼流程(SPA)

4.1 請求IdentityServer4的配置端點-獲取authorize端點

請求

...
GET /.well-known/openid-configuration HTTP/1.1\r\n
Host: localhost:5001\r\n
...

響應

200 OK
Date: Sun, 12 Jul 2020 17:38:32 GMT
Content-Type: application/json; charset=UTF-8
{"issuer":"http://localhost:5001","jwks_uri":"http://localhost:5001/.well-known/openid-configuration/jwks","authorization_endpoint":"http://localhost:5001/connect/authorize","token_endpoint":"http://localhost:5001/connect/token","userinfo_endpoint":"http://localhost:5001/connect/userinfo","end_session_endpoint":"http://localhost:5001/connect/endsession","check_session_iframe":"http://localhost:5001/connect/checksession","revocation_endpoint":"http://localhost:5001/connect/revocation","introspection_endpoint":"http://localhost:5001/connect/introspect","device_authorization_endpoint":"http://localhost:5001/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["openid","profile","api1","offline_access"],"claims_supported":["sub","name","family_name","given_name","middle_name","nickname","preferred_username","profile","picture","website","gender","birthdate","zoneinfo","locale","updated_at"],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","password","urn:ietf:params:oauth:grant-type:device_code"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"id_token_signing_alg_values_supported":["RS256"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true}

這里主要是獲取授權端點,由oidc-client.js內部觸發。

4.2 請求authorize端點

請求

GET /connect/authorize?client_id=js&redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Fcallback.html&response_type=code&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&code_challenge=kz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E&code_challenge_method=S256&response_mode=query HTTP/1.1
Host: localhost:5001

響應

Status Code: 302
Location: http://localhost:5001/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c03bf3fc7fe230f0378%26code_challenge%3Dkz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E%26code_challenge_method%3DS256%26response_mode%3Dquery

請求授權端點,帶上了response_type,scope,profile,code_challenge,code_challenge_method,響應302重定向,

4.3 跳轉登錄頁

請求

GET /Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c03bf3fc7fe230f0378%26code_challenge%3Dkz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E%26code_challenge_method%3DS256%26response_mode%3Dquery HTTP/1.1
Host: localhost:5001

響應

Status Code: 200
HTTP/1.1 200 OK
<!DOCTYPE html>
<html lang="en">
</html>

重定向至登錄頁,響應登錄頁html

4.4 登錄操作

請求

POST /Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c
POST /Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c03bf3fc7fe230f0378%26code_challenge%3Dkz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E%26code_challenge_method%3DS256%26response_mode%3Dquery HTTP/1.1
Host: localhost:5001
Content-Type: application/x-www-form-urlencoded
ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Djs%26redirect_uri%3Dhttp%253A%252F%252Flocalhost%253A6003%252Fcallback.html%26response_type%3Dcode%26scope%3Dopenid%2520profile%2520api1%26state%3De9292cf7e4d04c03bf3fc7fe230f0378%26code_challenge%3Dkz0v3GRm8yFhmjoOYBWX2pg68N5waBd0hFVvbvuIT9E%26code_challenge_method%3DS256%26response_mode%3Dquery&Username=admin&Password=admin123456%21&button=login&__RequestVerificationToken=CfDJ8EX5EDLzf1lFsdLD-61W3Pr-hyJIt5MjRQdemuM-9ab4QyRWG2omEwKHp0SZhc2a_ZjsoJEzge4QvrP_zVvkfQHq5bAki5KFTo1upo251HiQvnsFMp4ptx0KnmoHxBbIjDhBQ2VRh4fQCyJJiM791Nk&RememberLogin=false

響應

Status Code: 302
Location: /connect/authorize/callback?client_id=js&redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Fcallback.html&response_type=code&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&code_challenge=kz0v3GRm8yFhm
Set-Cookie: idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803; path=/; samesite=none\r\n

登錄操作,在原有的參數基礎上,增加Username,Password,Post提交,響應302重定向,並Set-Cookie

4.5 驗證授權,回調返回授權碼

請求

GET /connect/authorize/callback?client_id=js&redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Fcallback.html&response_type=code&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&code_challenge=kz0v3GRm8yFhmjoOYBWX2pg68N5waBd0
Host: localhost:5001\r\n
Cookie:Cookie pair: idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803

響應

Status Code: 302
Location: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-dit
Set-Cookie: idsrv=CfDJ8EX5EDLzf1lFsdLD-61W3PpyiWLfH7YweLeh_CTmjIJRzGGwIfhBAMhZaVaL24gZ5erWj5v38WA2DjcIqPPnSphi70aR_HZ9qnShmYfKkzrSmGTptJSESS6tanbw2RggxRPraoLf6PbthzsNJ3QZqZpB9AWJRxVvKtmq7-YFC-Kv11KHE_UHPBM3Hkpmulb35BqJ2wTumJwo8HdcJJdCPnzY0UqWCkv9wyUhQ2djvAzNceJKFiigwFsGWubmVuCse5hvp_QYLmwRdsobPP-0gSk7GnvCd6-r3ybcxYdvy2m_2XjpG1V3h34zefvLDw4sLeofllFJ5L4PUVhnqCd7DyKGb_xMAjHXvDiKgR32eCX9EZ8k1WsZsbH0NsX0xEKOlRboNNWM6mw_LXD3b_hZhDR00UbAmfjXapU6Sjss65HIPJXMhJ9HrVUleoNeFVUW-I61kd-FjXal9UxxOjv_cc5sYeIGxTnNkfT1Nc4wqQaip8ulkK0O2xYJWjPMeR-VA6rdwsvOa_iAq9fqY5KF2t4AzSoOMb62O2nwzqZZU1lpHxB5x86D0EjRUVnoR9CLfQ; path=/; samesite=none; httponly

驗證用戶名、密碼,服務端cookie已設置,驗證成功,再次響應302,重定向客戶端頁面callback.html,並Set-Cookie

4.6 請求IdentityServer4的配置端點-獲取token端點

請求

GET /.well-known/openid-configuration HTTP/1.1
Host: localhost:5001
Sec-Fetch-Mode: cors
Referer: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F

響應

200 OK
Date: Sun, 12 Jul 2020 17:38:32 GMT
Content-Type: application/json; charset=UTF-8
{"issuer":"http://localhost:5001","jwks_uri":"http://localhost:5001/.well-known/openid-configuration/jwks","authorization_endpoint":"http://localhost:5001/connect/authorize","token_endpoint":"http://localhost:5001/connect/token","userinfo_endpoint":"http://localhost:5001/connect/userinfo","end_session_endpoint":"http://localhost:5001/connect/endsession","check_session_iframe":"http://localhost:5001/connect/checksession","revocation_endpoint":"http://localhost:5001/connect/revocation","introspection_endpoint":"http://localhost:5001/connect/introspect","device_authorization_endpoint":"http://localhost:5001/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["openid","profile","api1","offline_access"],"claims_supported":["sub","name","family_name","given_name","middle_name","nickname","preferred_username","profile","picture","website","gender","birthdate","zoneinfo","locale","updated_at"],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","password","urn:ietf:params:oauth:grant-type:device_code"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"id_token_signing_alg_values_supported":["RS256"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true}

獲取token端點值

4.7 請求token端點

請求

POST /connect/token HTTP/1.1
Host: localhost:5001
Content-Type: application/x-www-form-urlencoded
Sec-Fetch-Mode: cors
Referer: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F
Accept-Encoding: gzip, deflate, br
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8

client_id=js&code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Fcallback.html&code_verifier=e661671994bd4eb89bb9e4da214e34f0970c24c9a36e41fe9e05aec433c604c2f721da29216345b0898445e5e105c86f&grant_type=authorization_code

響應

Status Code: 200
{
	"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc2OEFDMTBCNEEzNzI4RTIwNjQ0MDU4QjZFREY1MDUxIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1OTQ1NzU1MTIsImV4cCI6MTU5NDU3NTgxMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiYXVkIjoianMiLCJpYXQiOjE1OTQ1NzU1MTIsImF0X2hhc2giOiJhaU9rX05xeDh0OERXeTc5cFo3WFhBIiwic19oYXNoIjoiWkllczRfaldVMEFBaTI0ZUY4Yjg1dyIsInNpZCI6IjNDRUFCQkI2MkJFMUY2REREN0I3OTNBOEY1QkYxODAzIiwic3ViIjoiMSIsImF1dGhfdGltZSI6MTU5NDU3NTUxMiwiaWRwIjoibG9jYWwiLCJhbXIiOlsicHdkIl19.VmdopUrWH4rcrJp3nXcE-LLNodowQh6n0-HjN_aZZNOj1xPECwG_g0-nY9N9q-jNeapkIrWk2U2Y9liUXuBAOLHhT0Txou4dNhAdMIvYJ8SKRSgh06SEbpGT_hDtN345YZd9IJSjGWo3q_B04p03pw6S92tQp6ae74v1mMAgCskVnKth0SWpPqUPSuZjSdlcuzhA7OvXlz3wmeGJPu5c0jC1BBzrrM1_WmFvUCmwCo9q3Z0MLcz6eq1JZafhSkkRSAgTJdWIdrq6w7Yj1DInETebOhJrt3Yl7jGVAjJqK1WMnJym3J4n9d5GYfv9wA4eu3GgKvG_rax1GjgtV3zR0g",
	"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc2OEFDMTBCNEEzNzI4RTIwNjQ0MDU4QjZFREY1MDUxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTQ1NzU1MTIsImV4cCI6MTU5NDU3OTExMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiY2xpZW50X2lkIjoianMiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTk0NTc1NTEyLCJpZHAiOiJsb2NhbCIsImp0aSI6IkU3OTdDOTI0QUJFOTI1QTQ3N0Y1NDVCQUVFRkRGMUE3Iiwic2lkIjoiM0NFQUJCQjYyQkUxRjZEREQ3Qjc5M0E4RjVCRjE4MDMiLCJpYXQiOjE1OTQ1NzU1MTIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJhcGkxIl0sImFtciI6WyJwd2QiXX0.Cl4u3bU9bm0mG9qjn52WwstbPmuhBetKkEIRgVENIU_4hurJ8fRPiNc3zzl0tzgIw0_yvHy8eyA6EyVfSMyQZ77ao0TjEkBcTu62H7eKTHWrdKyp0eEhcxRiVvAYAQcFP2NPva8z0zZiVUUnE0q6-WiE7P_hDF8Ljs6AyYAS4khWX9iG-WoSlqDlalOo7ohU7gGleIpnlH5LUvQpkDVHbOzCNviJH6r4VbqT7llnIDNNjMwy9cwh3TJcYNsFZjTL3jsQtmbNv9ajmNBZKhWvGSRN_6ywgbPcL54FEqVTe3hfwdcSjHCSV2Owzcu6at8UplAm-Kd0T09ay6ChXWz67g",
	"expires_in": 3600,
	"token_type": "Bearer",
	"scope": "openid profile api1"
}

codescopeclient_idcode_verifiergrant_type等必要參數作為post參數請求token端點,返回id_tokenaccess_tokenexpires_inscope等.

4.8 請求userinfo端點

預檢請求

OPTIONS /connect/userinfo HTTP/1.1
Host: localhost:5001
Referer: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F

響應

Status Code: 204
Access-Control-Allow-Headers: authorization\r\n
Access-Control-Allow-Methods: GET\r\n
Access-Control-Allow-Origin: http://localhost:6003\r\n

正式請求

Host: localhost:5001
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc2OEFDMTBCNEEzNzI4RTIwNjQ0MDU4QjZFREY1MDUxIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE1OTQ1NzU1MTIsImV4cCI6MTU5NDU3OTExMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiY2xpZW50X2lkIjoianMiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNTk0NTc1NTEyLCJpZHAiOiJsb2NhbCIsImp0aSI6IkU3OTdDOTI0QUJFOTI1QTQ3N0Y1NDVCQUVFRkRGMUE3Iiwic2lkIjoiM0NFQUJCQjYyQkUxRjZEREQ3Qjc5M0E4RjVCRjE4MDMiLCJpYXQiOjE1OTQ1NzU1MTIsInNjb3BlIjpbIm9wZW5pZCIsInByb2ZpbGUiLCJhcGkxIl0sImFtciI6WyJwd2QiXX0.Cl4u3bU9bm0mG9qjn52WwstbPmuhBetKkEIRgVENIU_4hurJ8fRPiNc3zzl0tzgIw0_yvHy8eyA6EyVfSMyQZ77ao0TjEkBcTu62H7eKTHWrdKyp0eEhcxRiVvAYAQcFP2NPva8z0zZiVUUnE0q6-WiE7P_hDF8Ljs6AyYAS4khWX9iG-WoSlqDlalOo7ohU7gGleIpnlH5LUvQpkDVHbOzCNviJH6r4VbqT7llnIDNNjMwy9cwh3TJcYNsFZjTL3jsQtmbNv9ajmNBZKhWvGSRN_6ywgbPcL54FEqVTe3hfwdcSjHCSV2Owzcu6at8UplAm-Kd0T09ay6ChXWz67g
Referer: http://localhost:6003/callback.html?code=4E902EE0335BDED6D89E3775C836D3BC42848124BD1B0FCFA688CB91731C2DD5&scope=openid%20profile%20api1&state=e9292cf7e4d04c03bf3fc7fe230f0378&session_state=MP5-ftEtQxdTRi7Pw1a6HnU-ditlWq7kYgZrgydLbAQ.C34DCC1705940F3F7C5D34685AB51C9F
Accept-Encoding: gzip, deflate, br
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8

響應

Status Code: 200
{
	"name": "RandyField",
	"given_name": "Randy",
	"family_name": [
		"Field",
		"Randy"
	],
	"website": "http://www.randyfield.cn",
	"sub": "1"
}

4.9 請求checksession端點

請求

GET /connect/checksession HTTP/1.1
Host: localhost:5001
Cookie: .AspNetCore.Antiforgery.oLdxsluyV7s=CfDJ8EX5EDLzf1lFsdLD-61W3PplD2jEFwzyz4r_NCYMSVOLnWqf5HrxwUtY6ouOI2VHKD9vJdF48KUqkNQeTHXe3hrm8uf1GL_v1917E6-Km7WF3V06G4cBHNvrlJQuQF7k__7FPhPVySgmKgwqfAPky8w; idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803; idsrv=CfDJ8EX5EDLzf1lFsdLD-61W3PpyiWLfH7YweLeh_CTmjIJRzGGwIfhBAMhZaVaL24gZ5erWj5v38WA2DjcIqPPnSphi70aR_HZ9qnShmYfKkzrSmGTptJSESS6tanbw2RggxRPraoLf6PbthzsNJ3QZqZpB9AWJRxVvKtmq7-YFC-Kv11KHE_UHPBM3Hkpmulb35BqJ2wTumJwo8HdcJJdCPnzY0UqWCkv9wyUhQ2djvAzNceJKFiigwFsGWubmVuCse5hvp_QYLmwRdsobPP-0gSk7GnvCd6-r3ybcxYdvy2m_2XjpG1V3h34zefvLDw4sLeofllFJ5L4PUVhnqCd7DyKGb_xMAjHXvDiKgR32eCX9EZ8k1WsZsbH0NsX0xEKOlRboNNWM6mw_LXD3b_hZhDR00UbAmfjXapU6Sjss65HIPJXMhJ9HrVUleoNeFVUW-I61kd-FjXal9UxxOjv_cc5sYeIGxTnNkfT1Nc4wqQaip8ulkK0O2xYJWjPMeR-VA6rdwsvOa_iAq9fqY5KF2t4AzSoOMb62O2nwzqZZU1lpHxB5x86D0EjRUVnoR9CLfQ

響應

Status Code: 200
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
<!--Copyright (c) Brock Allen & Dominick Baier. All rights reserved.-->
<!--Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.-->
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
<title>Check Session IFrame</title>
</head>
<body>
</body>
</html>

之后又請求了一次/.well-known/openid-configuration,/connect/checksession

第一次與第二次/connect/checksession幾乎一樣的請求與響應,唯一的差別

從結果推導過程,第一次,是回調頁面callback.html,請求了/connect/checksession,獲取了一個iframe的html,callback.html中window.location = "index.html";由會重定向到index.html,可以推導出,這里是幫index.html請求渲染內容,因為index中有一個隱藏的iframe,如圖

兩次checksession之間還有一次/.well-known/openid-configuration請求,這次Referer已經是index.html,說明已經發生重定向了,重定向之后,由於document中有這個iframe,自然就會再發起一次請求,所以這兩次的Referer參數不一樣。

4.10 注銷登錄操作

①.請求endsession端點

請求
GET /connect/endsession?id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc2OEFDMTBCNEEzNzI4RTIwNjQ0MDU4QjZFREY1MDUxIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1OTQ1NzU1MTIsImV4cCI6MTU5NDU3NTgxMiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiYXVkIjoianMiLCJpYXQiOjE1OTQ1NzU1MTIsImF0X2hhc2giOiJhaU9rX05xeDh0OERXeTc5cFo3WFhBIiwic19oYXNoIjoiWkllczRfaldVMEFBaTI0ZUY4Yjg1dyIsInNpZCI6IjNDRUFCQkI2MkJFMUY2REREN0I3OTNBOEY1QkYxODAzIiwic3ViIjoiMSIsImF1dGhfdGltZSI6MTU5NDU3NTUxMiwiaWRwIjoibG9jYWwiLCJhbXIiOlsicHdkIl19.VmdopUrWH4rcrJp3nXcE-LLNodowQh6n0-HjN_aZZNOj1xPECwG_g0-nY9N9q-jNeapkIrWk2U2Y9liUXuBAOLHhT0Txou4dNhAdMIvYJ8SKRSgh06SEbpGT_hDtN345YZd9IJSjGWo3q_B04p03pw6S92tQp6ae74v1mMAgCskVnKth0SWpPqUPSuZjSdlcuzhA7OvXlz3wmeGJPu5c0jC1BBzrrM1_WmFvUCmwCo9q3Z0MLcz6eq1JZafhSkkRSAgTJdWIdrq6w7Yj1DInETebOhJrt3Yl7jGVAjJqK1WMnJym3J4n9d5GYfv9wA4eu3GgKvG_rax1GjgtV3zR0g&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A6003%2Findex.html 
Host: localhost:5001
Connection: keep-alive
Cookie: .AspNetCore.Antiforgery.oLdxsluyV7s=CfDJ8EX5EDLzf1lFsdLD-61W3PplD2jEFwzyz4r_NCYMSVOLnWqf5HrxwUtY6ouOI2VHKD9vJdF48KUqkNQeTHXe3hrm8uf1GL_v1917E6-Km7WF3V06G4cBHNvrlJQuQF7k__7FPhPVySgmKgwqfAPky8w; idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803; idsrv=CfDJ8EX5EDLzf1lFsdLD-61W3PpyiWLfH7YweLeh_CTmjIJRzGGwIfhBAMhZaVaL24gZ5erWj5v38WA2DjcIqPPnSphi70aR_HZ9qnShmYfKkzrSmGTptJSESS6tanbw2RggxRPraoLf6PbthzsNJ3QZqZpB9AWJRxVvKtmq7-YFC-Kv11KHE_UHPBM3Hkpmulb35BqJ2wTumJwo8HdcJJdCPnzY0UqWCkv9wyUhQ2djvAzNceJKFiigwFsGWubmVuCse5hvp_QYLmwRdsobPP-0gSk7GnvCd6-r3ybcxYdvy2m_2XjpG1V3h34zefvLDw4sLeofllFJ5L4PUVhnqCd7DyKGb_xMAjHXvDiKgR32eCX9EZ8k1WsZsbH0NsX0xEKOlRboNNWM6mw_LXD3b_hZhDR00UbAmfjXapU6Sjss65HIPJXMhJ9HrVUleoNeFVUW-I61kd-FjXal9UxxOjv_cc5sYeIGxTnNkfT1Nc4wqQaip8ulkK0O2xYJWjPMeR-VA6rdwsvOa_iAq9fqY5KF2t4AzSoOMb62O2nwzqZZU1lpHxB5x86D0EjRUVnoR9CLfQ
響應
Status Code: 302
Location: http://localhost:5001/Account/Logout?logoutId=CfDJ8EX5EDLzf1lFsdLD-61W3Pq-tO1b1UEAB4vvmYhY4sokyl8em0HlVYGX9otqLmnglBnG6V_RukkVflCbku5Elb72VsJkntUDrh6G1AP1ctnkCWhiuN9lIBouTf9swekyYrj8H0Q-5iHISwsYmXz00kEPqWR1-7-DoXbv2g3Dxtt3fPxVN5WmFd0-I7zuLoyPrpiqz62TYGUNygB1qOt0BXsvwVLWyl_amuMVbqUgJkvkbS4049YYVK7W0fl55L66mDnBEF5ktdixHE9ld3_dso-4FL8ppa-wdUq9Wy6JPo5p1S4BIf_LCfX0Cp4eDVow5sgVtpPfbamRX-pRHco_-H8jifrDg5xVmxupY6NAMgzK8Sbn6lZhW_KjkkpTyWRiE7kfc_uaYAeykfDGqvmAfGq7-9suIwGd3vvJw2-pn6aVuzTH4o3SycophXWv-5BUYQ

請求服務端endsession端點,傳遞參數id-token,響應302重定向注銷登錄頁面。

②.請求Logout頁面

請求
GET /Account/Logout?logoutId=CfDJ8EX5EDLzf1lFsdLD-61W3Pq-tO1b1UEAB4vvmYhY4sokyl8em0HlVYGX9otqLmnglBnG6V_RukkVflCbku5Elb72VsJkntUDrh6G1AP1ctnkCWhiuN9lIBouTf9swekyYrj8H0Q-5iHISwsYmXz00kEPqWR1-7-DoXbv2g3Dxtt3fPxVN5WmFd0-I7zuLoyPrpiqz62TYGUNygB1qOt0BXsvwVLWyl_amuMVbqUgJkvkbS4049YYVK7W0fl55L66mDnBEF5ktdixHE9ld3_dso-4FL8ppa-wdUq9Wy6JPo5p1S4BIf_LCfX0Cp4eDVow5sgVtpPfbamRX-pRHco_-H8jifrDg5xVmxupY6NAMgzK8Sbn6lZhW_KjkkpTyWRiE7kfc_uaYAeykfDGqvmAfGq7-9suIwGd3vvJw2-pn6aVuzTH4o3SycophXWv-5BUYQ HTTP/1.1
Host: localhost:5001
Cookie: .AspNetCore.Antiforgery.oLdxsluyV7s=CfDJ8EX5EDLzf1lFsdLD-61W3PplD2jEFwzyz4r_NCYMSVOLnWqf5HrxwUtY6ouOI2VHKD9vJdF48KUqkNQeTHXe3hrm8uf1GL_v1917E6-Km7WF3V06G4cBHNvrlJQuQF7k__7FPhPVySgmKgwqfAPky8w; idsrv.session=3CEABBB62BE1F6DDD7B793A8F5BF1803; idsrv=CfDJ8EX5EDLzf1lFsdLD-61W3PpyiWLfH7YweLeh_CTmjIJRzGGwIfhBAMhZaVaL24gZ5erWj5v38WA2DjcIqPPnSphi70aR_HZ9qnShmYfKkzrSmGTptJSESS6tanbw2RggxRPraoLf6PbthzsNJ3QZqZpB9AWJRxVvKtmq7-YFC-Kv11KHE_UHPBM3Hkpmulb35BqJ2wTumJwo8HdcJJdCPnzY0UqWCkv9wyUhQ2djvAzNceJKFiigwFsGWubmVuCse5hvp_QYLmwRdsobPP-0gSk7GnvCd6-r3ybcxYdvy2m_2XjpG1V3h34zefvLDw4sLeofllFJ5L4PUVhnqCd7DyKGb_xMAjHXvDiKgR32eCX9EZ8k1WsZsbH0NsX0xEKOlRboNNWM6mw_LXD3b_hZhDR00UbAmfjXapU6Sjss65HIPJXMhJ9HrVUleoNeFVUW-I61kd-FjXal9UxxOjv_cc5sYeIGxTnNkfT1Nc4wqQaip8ulkK0O2xYJWjPMeR-VA6rdwsvOa_iAq9fqY5KF2t4AzSoOMb62O2nwzqZZU1lpHxB5x86D0EjRUVnoR9CLfQ
響應
Status Code: 200
Content-Type: text/html; charset=utf-8
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: idsrv.session=.; expires=Fri, 12 Jul 2019 17:38:40 GMT; path=/; samesite=none
Set-Cookie: idsrv=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=none; httponly
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no" />
    <title>IdentityServer4</title>  
    <link rel="icon" type="image/x-icon" href="/favicon.ico" />
    <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
    <link rel="stylesheet" href="/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="/css/site.css" />
</head>
<body>  
<div class="nav-page">
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <a href="/" class="navbar-brand">
            <img src="/icon.png" class="icon-banner">
            IdentityServer4
        </a> 
    </nav>
</div>
    <div class="container body-container">      
<div class="logged-out-page">
    <h1>
        Logout
        <small>You are now logged out</small>
    </h1>
        <div>
            Click <a class="PostLogoutRedirectUri" href="http://localhost:6003/index.html">here</a> to return to the
            <span>JavaScript Client</span> application.
        </div>
        <iframe width="0" height="0" class="signout" src="http://localhost:5001/connect/endsession/callback?endSessionId=CfDJ8EX5EDLzf1lFsdLD-61W3Ppjdh7zkU7fvaRvtKK_djZ_wTALkKC4YyDjpZmbnIsfrQa2BTfaRz1AkiCNlJLVYT2mUZA0Os9WyOBB1QDhYOjscWlomm6RWpUDy4L8tjr1mTdkH0T5IXOUsF7wcRrP-6ssERsPyswxqg9bFwkjuYlZnQYZOFSdFwGV8T3uru7BVhP8HywA6JeyYdUC-CQl_02vdiN3h5_bfxcP6sSbuu0kq8Dvs1GRXZT2B813Wy7DI_fmnWwyMiy2isjdpZjxx2E"></iframe>
</div>
    </div>
    <script src="/lib/jquery/dist/jquery.slim.min.js"></script>
    <script src="/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

重定向至Logout頁面,並通過Set-CookieIndentityServer-localhost:5001的cookie置為空,且響應一段包含隱藏src="http://localhost:5001/connect/endsession/callback"的iframe的html

③.觸發endsession回調

請求
GET /connect/endsession/callback?endSessionId=CfDJ8EX5EDLzf1lFsdLD-61W3Ppjdh7zkU7fvaRvtKK_djZ_wTALkKC4YyDjpZmbnIsfrQa2BTfaRz1AkiCNlJLVYT2mUZA0Os9WyOBB1QDhYOjscWlomm6RWpUDy4L8tjr1mTdkH0T5IXOUsF7wcRrP-6ssERsPyswxqg9bFwkjuYlZnQYZOFSdFwGV8T3uru7BVhP8HywA6JeyYdUC-CQl_02vdiN3h5_bfxcP6sSbuu0kq8Dvs1GRXZT2B813Wy7DI_fmnWwyMiy2isjdpZjxx2E HTTP/1.1
Host: localhost:5001
Sec-Fetch-Dest: iframe
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-
Cookie: .AspNetCore.Antiforgery.oLdxsluyV7s=CfDJ8EX5EDLzf1lFsdLD-61W3PplD2jEFwzyz4r_NCYMSVOLnWqf5HrxwUtY6ouOI2VHKD9vJdF48KUqkNQeTHXe3hrm8uf1GL_v1917E6-Km7WF3V06G4cBHNvrlJQuQF7k__7FPhPVySgmKgwqfAPky8w
響應
Status Code: 200
Date: Sun, 12 Jul 2020 17:38:39 GMT
Content-Type: text/html; charset=UTF-8
Server: Kestrel
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache
Transfer-Encoding: chunked
Content-Security-Policy: default-src 'none'; style-src 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY='
X-Content-Security-Policy: default-src 'none'; style-src 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY='
<!DOCTYPE html><html><style>iframe{display:none;width:0;height:0;}</style><body></body></html>

觸發回調,cookie已置為空

5.總結

這里面確實是PKCE的授權碼模式,其次整個客戶端(SPA)與服務端交互過程有很多騷操作,比如在html里面返回一段隱藏的iframe,從而觸發回調。下一篇,我們將會繼續討論在MVC應用中的IdentityServer4授權碼流程,同樣是PKCE,但是同樣具有一些奇技淫巧的騷操作,待你我共賞。

對了,有個小貼士,網易有道詞典跟WireShark有沖突,打開就是未響應,使用時,關閉有道詞典即可,這個坑簡直了,真是王大錘的萬萬沒想到。

參考鏈接

https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html

https://www.zhihu.com/question/31896659

https://tonyxu.io/zh/posts/2018/oauth2-pkce-flow/

https://www.cnblogs.com/hubwang2020/p/12671712.html


作者:Garfield

同步更新至個人博客:http://www.randyfield.cn/

本文版權歸作者所有,未經許可禁止轉載,否則保留追究法律責任的權利,若有需要請聯系287572291@qq.com


免責聲明!

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



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