主頁 > .NET開發 > 從零搭建一個IdentityServer——會話管理與登出

從零搭建一個IdentityServer——會話管理與登出

2021-04-09 06:02:57 .NET開發

在上一篇文章中我們介紹了單頁應用是如何使用IdentityServer完成身份驗證的,并且在講到靜默登錄以及會話監聽的時候都提到會話(Session)這一概念,會話指的是用戶與系統之間互動程序,反過來說就是用戶與系統之間互動的狀態就保存在會話(Session)中,對于HTTP協議來說,由于它本身是無狀態的,所以為了能夠記錄用戶訪問系統的狀態,一般使用Cookie來存放會話資訊,但是現在我們需要保存的是與IdentityServer之間的會話,對于單頁應用來說它一般會存在跨域問題,那IdentityServer是如何處理跨域來完成會話管理的呢?同時IdentityServer4又提供了哪些與登錄登出相關的特性?本文就從會話管理開始來一一介紹,   本文內容有:
  • 會話管理
  • 前端登出
    • 授權服務器(OP)登出聯動客戶端(RP)
    • 原理決議
    • 客戶端(RP)登出聯動授權服務器(OP)
  • 后端登出
  • 小結

會話管理

  首先會話本身有兩個主體,即服務器和客戶端,服務端就是identityServer本身,它是一個asp.net core應用程式,那么實際上它的會話機制就和普通的asp.net core應用程式是一致的,通過cookie來保存相應會話的id或資訊,   下圖為登錄IdentityServer后瀏覽器端存盤的會話資訊和身份資訊:   而對于客戶端來說,我們知道IdentityServer4實際上是OpenIDConnect(OIDC)協議的一個實作,而OIDC協議本身是沒有會話管理這一特性的,它的出現實際上是在一個補充協議中:https://openid.net/specs/openid-connect-session-1_0.html,該協議約定了客戶端如何對服務端的會話資訊進行管理,而協議的主要內容是以下幾個點:
  • 協議定義:如何持續監控終端用戶在OpenID Provider(OP,Identity Server)上提供的會話資訊,以便于終端用戶登出OpenID Provider(OP,IdentityServer)時能夠同時登出客戶端(Relying Party),
  關于OP(IdentityServer)和RP(client)見下圖:      簡單來說就是上一篇文章演示的“會話監控”內容,當用戶直接從IdentityServer直接登出時,客戶端本身能夠感知到并作出相應動作(客戶端登出),
  • iframe:一個HTML的標簽,它代表一個內嵌的HTML檔案,如果在HTML使用iframe那就是檔案中包含另一個檔案,iframe可以通過src屬性來設定包含檔案的url地址,當iframe設定的url與主檔案的url不同域時,可以使用iframe的postmessage方法實作跨域通信,
  關于iframe及postmessage可參考:https://blog.csdn.net/tang_yi_/article/details/79401280
  • RP iframe:位于客戶端(Relying Party, RP)中的一個iframe,這個iframe的作用是用于向OP iframe發送及接收資訊,發送的資訊是用于告知OP iframe進行會話檢查,接收的資訊是OP iframe完成會話檢查后的結果,
  下圖是oidc-client.js中用于創建RP iframe的代碼:      下圖為使用RP iframe向OP iframe發送資訊的代碼:      下圖為接收到OP iframe會話驗證結果訊息后的處理代碼:   
  • OP iframe:一個由OpenID Provider(OP,IdentityServer)提供的,位于客戶端(Relying Party, RP)中的一個iframe,它的作用是與IdentityServer同域,保存于IdentityServer的會話資訊,并提供檢查介面(基于postmessage)的iframe,
  當用戶身份驗證成功后,oidc-client會根據配置資訊來訪問獲取OP iframe:      OP iframe請求:      下圖為OP iframe中監聽RP iframe會話檢查訊息,完成檢查并回傳訊息結果的代碼:      會話檢查是對用戶資料中包含的會話狀態(session_state)資訊進行核對,會話狀態(session_state)資訊分為兩個部分,它們用“.”分隔,前部分是客戶端id、客戶端域名、會話id加鹽計算出來的哈希值,后部分是哈希計算使用的鹽(salt),      下圖為會話檢查的具體邏輯,獲取當前的會話id并進行哈希計算后與用戶資訊中的哈希值進行核對,如果不一致那么認為會話發生變化,      發生變化后oidc-client會自動發起授權請求來確認新會話的資訊,這個也就是上一篇文章登出后發起的請求回傳需要登錄的原因:   從以上內容看來oidc協議的會話管理主要是通過iframe完成的,   下圖為單頁應用完成登錄后發起靜默登錄時候的頁面資訊:      圖中存在兩個iframe,第一個是OP iframe包含了會話檢查相關內容,第二個是發起靜默登錄時,創建的一個指向授權終結點的iframe,通過跨域完成登錄,需要注意的是由于RP iframe是通過js代碼創建的,所以無法在頁面代碼中找到,   到此為止我們了解到的僅僅是會話管理在單頁應用中實作的登錄與登出功能,通過會話管理它可以將瀏覽器與客戶端(RP)及授權服務器(OP)之間的關系聯系起來,簡單來說就是當瀏覽器與授權服務器(OP)會話中斷時客戶端(RP)程式能夠知道(會話資訊改變),同時如果瀏覽器與客戶端(RP)會話中斷時授權服務器(OP)也能知道(先清除客戶端身份資訊,然后跳轉到授權服務器登出界面), 其次還有一個特點就是由于OIDC的會話管理協議是使用iframe來完成跨域會話檢查,雖然默認檢查頻率是2秒一次,但是它不需要向授權服務器發送任何請求即可完成檢查,所以可以節省大量的網路資源和服務器資源,   但最后看來這個會話管理協議只適用于單頁應用來完成相關功能,但是對于web應用來說,使用單頁方式實作的僅僅是一部分,其它方式是如何處理客戶端(RP)與授權服務器(OP)之間的登錄聯系的呢?

前端登出

  OIDC前端登出協議(OpenID Connect Front-Channel Logout),這個協議提供了一種登出的機制,該機制是通過瀏覽器的前端技術來與被登出的客戶端(RP)/服務器(OP)建立通信,不再需要iframe就可以實作相關登出功能,具體協議內容參見:https://openid.net/specs/openid-connect-frontchannel-1_0.html   接下來我們就通過asp.net core應用程式來演示一下這個協議是如何完成前端登出的,

授權服務器(OP)登出聯動客戶端(RP)

  1. API專案中添加一個登出頁面   API專案實際上就是我們的客戶端(RP),當前的例子就是通過在該應用上添加一個登出頁面來完成授權服務器登出后通知客戶端登出的功能,   注:asp.net core api專案實際上是不包含頁面的,此處僅為了方便通過api專案中添加Razor頁面來完成演示,   首先添加一個Razor頁面的布局:   完成后獲得相關的目錄結構和必要檔案:      添加一個登出頁面:      后端代碼,代碼非常簡單,就是通過get方法訪問該頁面時就直接進行登出操作:      最后在Startup檔案中添加Razor Page的服務和路由:   

   

  然后運行程式即可訪問到代碼了:      2. 授權服務器中創建一個前端登出頁面,同時對Identity登出頁面改造:   在本系列文章前面我們通過IdentityServer4集成asp.net core identity實作了用戶的登錄登出功能,并且在使用中也暫時沒發現任何問題,可以滿足基礎的授權服務器的登錄和登出,但是如果要實作登出聯動,那么就需要進行一些改造,   主要改造有下面幾個步驟:   1)添加一個前端登出頁面:      2)對前端登出的Razor Page的后端Model中添加三個欄位,并且用特性標明它們從Query中獲取:      3)在前端登出的Razor Page的前端代碼中添加以下代碼:      4)修改Identity登出頁面的后端Post請求處理方法:      3. 修改客戶端資料,添加uri(客戶端新增的登出地址):      4. 驗證登出聯動:   首先通過IdentityServer完成身份驗證,并可訪問受保護資源:      然后開啟新的選項卡訪問IdentityServer的登出頁面,此時因為客戶端程式是通過客戶端完成了授權服務器的身份驗證,在瀏覽器會話資訊保存期間,它默認是登錄狀態:   最后我們點擊登出鏈接,程式將攜帶相關引數跳轉到我們添加的前端登出頁面:   現在我們再去重繪受保護資源時得到以下結果,它跳轉到授權服務器的登錄頁面了,這意味著我們在授權服務器(OP)登出的時候,客戶端(RP)同時也完成了登出:

原理簡析

  它們是如何完成聯動登出的呢?我們首先來分析一下相關主體有哪些:
  • 客戶端(RP)登出頁面:訪問該頁面即可完成客戶端(RP)方面的登出,這個頁面用于授權服務器登出聯動時訪問,
  • 授權服務器(OP)登出頁面:一個基于Asp.net core Identity的登出頁面,用于asp.net core應用程式(這里特指授權服務器)的登出,
  • 授權服務器(OP)前端登出頁面:一個用于完成OIDC前端登出協議的登出頁面,負責客戶端登出頁面的呼叫及客戶端應用程式跳轉(該頁面功能有點類似于,我們在購買火車票付款時,首先跳轉到支付頁面,完成支付后通知系統已支付,并且又跳轉回訂單頁面的程序),
   其次在整個程序中我們還使用了兩個比較重要的組件:
  • IdentityServer4的互動服務(Interaction Service):這個實際上就是identityServer4提供的一組介面,這些介面約定了用戶與IdentityServer4的互動方法,該介面可以通過依賴注入的方式進行使用,在本例中使用Interaction Service的目的是獲取當前登錄用戶的登出背景關系,以便完成后續登出作業(相關資訊存盤于Cookie中,類似基于Cookie身份驗證的身份資訊載體),關于介面內容詳見檔案:https://identityserver4.readthedocs.io/en/latest/reference/interactionservice.html
  • 結束會話終結點(End Session Endpoint):就是字面意思,結束會話使用的終結點,在這里的作用是通過結束會話終結點來終結會話并跳轉到客戶端(RP)的登出頁面完成客戶端(RP)登出,
  它的整個登出流程如下圖所示:      簡單來說就是當用戶訪問授權服務器登出頁面并進行登出操作后,它進行授權服務應用登出后,跳轉到前端登錄頁面,通過登出背景關系資訊渲染了一個iframe元素,通過iframe完成結束會話終結點的訪問和客戶端登出頁面的訪問,最終呈現給用戶的就是前端登出頁面,   下圖為登出操作后的網路請求詳情:   整個程式由登出頁面攜帶引數重定向到請求1(前端登錄頁面),然后通過前端登錄頁面的iframe發起請求2(結束會話終結點請求),最后再由結束會話終結點請求中的iframe完成客戶端登出請求3, 下圖為前端登錄頁面在執行完成以上內容后的結果,從結果中我們可以看到兩個iframe分別對應了結束會話終結點請求和客戶端登出頁面請求:      總的來說就是三個要點:   1. 清除授權服務器的身份資訊,   2. 結束IdentityServer4的會話狀態,   3. 清除客戶端的身份資訊,

客戶端(RP)登出聯動授權服務器(OP)

  以上面所提到的三個要點來看如何實作客戶端(RP)與授權服務器(OP)的登出聯動,   首先我們在客戶端添(RP)加一個登出頁面:      在頁面后臺代碼中添加以下內容(主要是獲取id token然后拼接授權服務器的結束會話終結點地址,另外就是退出登錄):      以下是頁面前端代碼,主要是通過iframe去訪問結束會話終結點(注:使用iframe的目的是因為訪問授權服務器時能夠攜帶相關Cookie,以便進行身份驗證及登出操作):      最后修改一下授權服務器(OP)的登出頁面后臺代碼,當接收到攜帶logoutId的Get請求時,對用戶進行登出操作(注:最后一句對User賦值的代碼,是因為雖然應用程式執行了登出,但是User.Identity.IsAuthenticated仍然為true,這里有找到一些資料可以進行參考:https://stackoverflow.com/questions/10663873/user-identity-isauthenticated-true-after-logout-asp-net-mvc https://www.codeproject.com/tips/1115491/request-isauthenticated-is-always-true-after-call):      接下來就開始驗證我們的聯動登出,首先確保受保護資源可訪問:      然后訪問客戶端的登出頁面(https://localhost:51001/logoutwithop):   訪問登出頁面時,會觸發授權服務器的登出頁面代碼,從代碼中我們可以看到相應的logoutId以及通過IdentityServer4互動服務獲得的登出背景關系:      通過斷點后,我們可以看到整個請求程序(請忽略相關404鏈接,是因為沒有添加靜態檔案處理中間件導致的檔案無法獲取):   iframe里面的內容,可以看到授權服務器已經成功登出:      重繪受保護資源會跳轉到授權服務器進行身份驗證,這證明了客戶端本身已經完成登出:      以上內容就是客戶端(RP)聯動授權服務器(OP)的登出功能,總的來說還是三個要點:   1. 清除客戶端的身份資訊,   2. 結束IdentityServer4的會話狀態,   3. 清除授權服務器的身份資訊,   注:IdentityServer4中實際有兩個會話結束終結點,分別是EndSessionCallbackEndPoint和EndSessionEndPoint,前者用于OP聯動RP的登出,主要功能是渲染一個FrontChannelLogoutUrl的iframe來訪問客戶端的前端登出頁面,后者是用于RP聯動OP時發起的結束會話請求,這個請求identityServer會保存一個登出資訊,這個操作是EndSessionCallbackEndPoint不具備的,換句話說如果在OP聯動RP的場景下,客戶端(RP)的登出頁面(本例僅呼叫的HttpContext的Signout方法登出)還應該呼叫EndSessionEndPoint來給授權服務器保存登出資訊,本文為了簡化內容復雜性把兩個終結點都稱為了結束會話終結點,

后端登出

  前面提到的無論是會話管理還是前端登出,它都有一個共同點就是基于瀏覽器,因為瀏覽器可以通過Cookie或者H5的存盤功能來保存會話/狀態資訊,登出實際上就是把相應的資訊洗掉,這種情況下不管是客戶端(RP)還是授權服務器(OP)它們本身都只是去驗證身份資訊的有效性,如果身份資訊存在且有效那么身份驗證通過,但是實際應用中可能會出現這么一種情況,假設身份資訊過期時間足夠長,那么只要用戶不主動登出,那么身份資訊將永久保存、永久有效,服務端沒有“任何”一種方法能夠主動讓其失效,這是存在問題的,針對這種問題OIDC提出了后端登出這一概念,   后端登出是什么呢?它實際上是一種授權服務器(OP)與客戶端(RP)之間直接通信的登出機制,簡單說來就是當通過授權服務器(OP)登出時可以直接通知到客戶端(RP),不需要瀏覽器的支持,說個具體場景就類似于微信可以同時在PC以及移動設備上登錄,但是移動設備上可以直接控制PC登出,或者是當用戶修改密碼后,密碼修改前所有的會話都應被終止,   后端登出雖然不再基于瀏覽器的會話資訊,但是它畢竟需要明確知道相關登出的會話資訊,所以它本身比前端登出要復雜,需要授權服務器(OP)以及客戶端(RP)都支持會話管理,對于授權服務器來說可以通過訪問https://localhost:5001/.well-known/openid-configuration來確定是否支持后端登出:      而客戶端(RP)本身就得自己實作了,在實作客戶端的會話管理之前,還有一個概念需要了解一下,那就是登出令牌(Logout Token),它包含兩個比較重要的資訊,其一是用戶id(sub),其二是會話id(sid)具體參考檔案:https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken 擁有這兩個資訊,或者只有對這兩個資訊進行管理,那么在登出時我們才能知道到底是哪一個用戶的哪一次會話被結束了,那么LogoutToken是怎么來的呢?   首先我們在客戶端(RP)添加一個用于接收后端請求的控制器(注:需要Post方法):      然后將這個控制器的地址配置到IdentityServer的Client資料庫中:      運行程式并執行上面介紹過的前端登出(OP聯動RP登出流程),就會觸發后端登出,在相應代碼設定的斷點會被觸發:      在這個請求中我們發現Form表單中包含了logout_token:      根據格式看來logout_token是一個jwt,以jwt方式決議該token獲得結果如下:   其中包含了用戶id(sub)及此次會話id(sid),在此實驗基礎上,我們來實作一個簡單的客戶端會話管理,   添加一個登出會話管理型別,該型別維護一個登出會話串列,它的功能是當接收到后端登出請求時將相應登出資訊存盤到串列中,用戶在身份驗證后來判斷用戶及當前會話是否存在于串列,如果存在串列中,那么證明該用戶的當前會話已經被后端登出,應該被禁止:      修改后端登出控制器代碼(此代碼僅用于測驗,并未對任何例外情況進行處理,另外也未對token進行完整性驗證等,如果需要了解token驗證相關內容,可參考:https://github.com/IdentityServer/IdentityServer4/tree/main/samples/Clients/src/MvcHybridBackChannel):      添加一個Cookie身份驗證事件處理器,當用戶通過身份驗證時去判斷sub及sid是否已經被登出:      應用該事件處理器,先添加到容器,然后配置到Cookie身份驗證中:      為了保證能夠驗證后端登出有效性,我們把前端登出代碼注釋后,運行程式(還是按照前端登出OP聯動RP流程,但前端登出代碼已經被注釋而失效了,所以如果登出成功,那就是后端登出的效果):      當程式完成前端登出跳轉后,會自動觸發并進入登出流程:      相應的用戶及會話已經被登出,所以需要拒絕并登出用戶:      再次重繪受保護資源,程式將跳轉到授權服務器登錄頁面,換句話說就是后端登出成功,      以上就是后端登出內容(OP聯動RP進行后端登出),為什么沒有RP聯動OP的后端登出?因為在非瀏覽器環境下客戶端一般不會保存與授權服務器的身份驗證資訊(哪怕保存了,那么自己洗掉即可),所以自然就不存在RP登出需要聯動OP的場景,   另外要注意的是后端登出原本是在非瀏覽器環境下使用的,但上面的例子仍然是通過基于瀏覽器的前端登出來完成的,其目的僅僅是為了方便演示,其次后端登出請求是由結束會話回呼終結點(EndSessionCallback EndPoint)發起的(只要客戶端資訊存在BackChannelLogoutUri資訊就會自動發起),那么如果想主動發起該請求我們需要借助IBackChannelLogoutService來完成,該服務的SendLogoutNotificationsAsync方法可以通過用戶id、會話id以及客戶端id來發起相應客戶端的后端登出請求:      關于如何獲取會話資訊來通過該服務發起登出會在后續文章中介紹,

小結

  本文主要介紹了IdentityServer4的會話管理以及前后端登出功能,其中會話管理和前端登出都是基于瀏覽器,通過瀏覽器本身的Cookie及存盤功能來保存相關身份、會話資料,同時借助Iframe來實作跨域請求、跨域會話檢查等等功能,   對于前端登出來說它主要有授權服務器(OP)與客戶端(RP)互相聯動兩種場景,無論用戶從哪一方進行登出操作都能夠將兩方的身份資訊洗掉,   對于后端登出來說它要求授權服務器(OP)與客戶端(RP)雙方都具備后端登出功能,IdentityServer4本身支持,而客戶端就需要自己實作了,本文中實作了一個簡單的登出會話管理功能,即當用戶觸發后端登出后,客戶端會記錄登出資訊,當用戶再次發起請求時,在身份驗證(驗證Cookie,此時Cookie仍然有效)后,來判斷該用戶是否已經后端登出,如果已經登出則主動拒絕訪問,   PS.  這篇文章寫的時間跨度有點大,文章內容相對較多,并且有大量的檔案和代碼修改,但文中代碼均已圖片形式展現,本系列文章完結后會上傳相關代碼檔案,如有問題可隨時聯系作者,   參考: https://stackoverflow.com/questions/10663873/user-identity-isauthenticated-true-after-logout-asp-net-mvc https://www.codeproject.com/tips/1115491/request-isauthenticated-is-always-true-after-call https://openid.net/specs/openid-connect-session-1_0.html https://openid.net/developers/specs/ https://identityserver4.readthedocs.io/en/latest/reference/interactionservice.html https://openid.net/specs/openid-connect-rpinitiated-1_0.html https://github.com/IdentityServer/IdentityServer4/tree/main/samples/Clients/src/MvcHybridBackChannel   本文鏈接:https://www.cnblogs.com/selimsong/p/14630356.html 從零搭建一個IdentityServer——目錄(更新中...)

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/273950.html

標籤:.NET Core

上一篇:C# 將XML轉為Word(附VB.NET代碼)

下一篇:通過lms.samples熟悉lms微服務框架的使用

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more