主頁 > 後端開發 > 鑒權 5 兄弟:cookie、session、token、jwt、單點登錄,終于有人說清楚了!

鑒權 5 兄弟:cookie、session、token、jwt、單點登錄,終于有人說清楚了!

2022-01-19 16:46:44 後端開發

作者:Henrylulu
來源:juejin.cn/post/6898630134530752520

本文你將看到:

  • 基于 HTTP 的前端鑒權背景
  • cookie 為什么是最方便的存盤方案,有哪些操作 cookie 的方式
  • session 方案是如何實作的,存在哪些問題
  • token 方案是如何實作的,如何進行編碼和防篡改?jwt 是做什么的?refresh token 的實作和意義
  • session 和 token 有什么異同和優缺點
  • 單點登錄是什么?實作思路和在瀏覽器下的處理

從狀態說起

「HTTP 無狀態」我們知道,HTTP 是無狀態的,也就是說,HTTP 請求方和回應方間無法維護狀態,都是一次性的,它不知道前后的請求都發生了什么,但有的場景下,我們需要維護狀態,最典型的,一個用戶登陸微博,發布、關注、評論,都應是在登錄后的用戶狀態下的,「標記」那解決辦法是什么呢?::標記::,

在學校或公司,入學入職那一天起,會錄入你的身份、賬戶資訊,然后給你發個卡,今后在園區內,你的門禁、打卡、消費都只需要刷這張卡,

「前端存盤」 這就涉及到一發、一存、一帶,發好辦,登陸介面直接回傳給前端,存盤就需要前端想辦法了,

前提是,你要把卡帶在身上,

前端的存盤方式有很多,

  • 最矬的,掛到全域變數上,但這是個「體驗卡」,一次重繪頁面就沒了
  • 高端點的,存到 cookie、localStorage 等里,這屬于「會員卡」,無論怎么重繪,只要瀏覽器沒清掉或者過期,就一直拿著這個狀態,

前端存盤這里不展開了,有地方存了,請求的時候就可以拼到引數里帶給介面了,

基石:cookie

可是前端好麻煩啊,又要自己存,又要想辦法帶出去,有沒有不用操心的?

有,cookie,cookie 也是前端存盤的一種,但相比于 localStorage 等其他方式,借助 HTTP 頭、瀏覽器能力,cookie 可以做到前端無感知,一般程序是這樣的:

  • 在提供標記的介面,通過 HTTP 回傳頭的 Set-Cookie 欄位,直接「種」到瀏覽器上
  • 瀏覽器發起請求時,會自動把 cookie 通過 HTTP 請求頭的 Cookie 欄位,帶給介面

「配置:Domain / Path」

你不能拿清華的校園卡進北大,

cookie 是要限制::「空間范圍」::的,通過 Domain(域)/ Path(路徑)兩級,

Domain屬性指定瀏覽器發出 HTTP 請求時,哪些域名要附帶這個 Cookie,如果沒有指定該屬性,瀏覽器會默認將其設為當前 URL 的一級域名,比如 www.example.com 會設為 example.com,而且以后如果訪問example.com的任何子域名,HTTP 請求也會帶上這個 Cookie,如果服務器在Set-Cookie欄位指定的域名,不屬于當前域名,瀏覽器會拒絕這個 Cookie,Path屬性指定瀏覽器發出 HTTP 請求時,哪些路徑要附帶這個 Cookie,只要瀏覽器發現,Path屬性是 HTTP 請求路徑的開頭一部分,就會在頭資訊里面帶上這個 Cookie,比如,PATH屬性是/,那么請求/docs路徑也會包含該 Cookie,當然,前提是域名必須一致,—— Cookie — JavaScript 標準參考教程(alpha)

「配置:Expires / Max-Age」

你畢業了卡就不好使了,

cookie 還可以限制::「時間范圍」::,通過 Expires、Max-Age 中的一種,

Expires屬性指定一個具體的到期時間,到了指定時間以后,瀏覽器就不再保留這個 Cookie,它的值是 UTC 格式,如果不設定該屬性,或者設為null,Cookie 只在當前會話(session)有效,瀏覽器視窗一旦關閉,當前 Session 結束,該 Cookie 就會被洗掉,另外,瀏覽器根據本地時間,決定 Cookie 是否過期,由于本地時間是不精確的,所以沒有辦法保證 Cookie 一定會在服務器指定的時間過期,Max-Age屬性指定從現在開始 Cookie 存在的秒數,比如60 * 60 * 24 * 365(即一年),過了這個時間以后,瀏覽器就不再保留這個 Cookie,如果同時指定了Expires和Max-Age,那么Max-Age的值將優先生效,如果Set-Cookie欄位沒有指定Expires或Max-Age屬性,那么這個 Cookie 就是 Session Cookie,即它只在本次對話存在,一旦用戶關閉瀏覽器,瀏覽器就不會再保留這個 Cookie,—— Cookie — JavaScript 標準參考教程(alpha)

「配置:Secure / HttpOnly」

有的學校規定,不帶卡套不讓刷(什么奇葩學校,假設);有的學校不讓自己給卡貼貼紙,

cookie 可以限制::「使用方式」::,

Secure屬性指定瀏覽器只有在加密協議 HTTPS 下,才能將這個 Cookie 發送到服務器,另一方面,如果當前協議是 HTTP,瀏覽器會自動忽略服務器發來的Secure屬性,該屬性只是一個開關,不需要指定值,如果通信是 HTTPS 協議,該開關自動打開,HttpOnly屬性指定該 Cookie 無法通過 JavaScript 腳本拿到,主要是Document.cookie屬性、XMLHttpRequest物件和 Request API 都拿不到該屬性,這樣就防止了該 Cookie 被腳本讀到,只有瀏覽器發出 HTTP 請求時,才會帶上該 Cookie,—— Cookie — JavaScript 標準參考教程(alpha)

「HTTP 頭對 cookie 的讀寫」 回過頭來,HTTP 是如何寫入和傳遞 cookie 及其配置的呢?HTTP 回傳的一個 Set-Cookie 頭用于向瀏覽器寫入「一條(且只能是一條)」cookie,格式為 cookie 鍵值 + 配置鍵值,例如:

Set-Cookie: username=jimu; domain=jimu.com; path=/blog; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

那我想一次多 set 幾個 cookie 怎么辦?多給幾個 Set-Cookie 頭(一次 HTTP 請求中允許重復)

Set-Cookie: username=jimu; domain=jimu.com
Set-Cookie: height=180; domain=me.jimu.com
Set-Cookie: weight=80; domain=me.jimu.com

HTTP 請求的 Cookie 頭用于瀏覽器把符合當前「空間、時間、使用方式」配置的所有 cookie 一并發給服務端,因為由瀏覽器做了篩選判斷,就不需要歸還配置內容了,只要發送鍵值就可以,

Cookie: username=jimu; height=180; weight=80

「前端對 cookie 的讀寫」 前端可以自己創建 cookie,如果服務端創建的 cookie 沒加HttpOnly,那恭喜你也可以修改他給的 cookie,呼叫document.cookie可以創建、修改 cookie,和 HTTP 一樣,一次document.cookie能且只能操作一個 cookie,

document.cookie = 'username=jimu; domain=jimu.com; path=/blog; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly';

呼叫document.cookie也可以讀到 cookie,也和 HTTP 一樣,能讀到所有的非HttpOnly cookie,

console.log(document.cookie);
// username=jimu; height=180; weight=80

(就一個 cookie 屬性,為什么讀寫行為不一樣?get / set 了解下)「cookie 是維持 HTTP 請求狀態的基石」了解了 cookie 后,我們知道 cookie 是最便捷的維持 HTTP 請求狀態的方式,大多數前端鑒權問題都是靠 cookie 解決的,當然也可以選用別的存盤方式(后面也會多多少少提到),那有了存盤工具,接下來怎么做呢?

應用方案:服務端 session

現在回想下,你刷卡的時候發生了什么?

其實你的卡上只存了一個 id(可能是你的學號),刷的時候物業系統去查你的資訊、賬戶,再決定「這個門你能不能進」「這個雞腿去哪個賬戶扣錢」,

這種操作,在前后端鑒權系統中,叫 session,典型的 session 登陸/驗證流程:

  • 瀏覽器登錄發送賬號密碼,服務端查用戶庫,校驗用戶
  • 服務端把用戶登錄狀態存為 Session,生成一個 sessionId
  • 通過登錄介面回傳,把 sessionId set 到 cookie 上
  • 此后瀏覽器再請求業務介面,sessionId 隨 cookie 帶上
  • 服務端查 sessionId 校驗 session
  • 成功后正常做業務處理,回傳結果

「Session 的存盤方式」 顯然,服務端只是給 cookie 一個 sessionId,而 session 的具體內容(可能包含用戶資訊、session 狀態等),要自己存一下,存盤的方式有幾種:

  • Redis(推薦):記憶體型資料庫,redis中文官方網站,以 key-value 的形式存,正合 sessionId-sessionData 的場景;且訪問快,
  • 記憶體:直接放到變數里,一旦服務重啟就沒了
  • 資料庫:普通資料庫,性能不高,

「Session 的過期和銷毀」很簡單,只要把存盤的 session 資料銷毀就可以, 「Session 的分布式問題」 通常服務端是集群,而用戶請求過來會走一次負載均衡,不一定打到哪臺機器上,那一旦用戶后續介面請求到的機器和他登錄請求的機器不一致,或者登錄請求的機器宕機了,session 不就失效了嗎?這個問題現在有幾種解決方式,

  • 一是從「存盤」角度,把 session 集中存盤,如果我們用獨立的 Redis 或普通資料庫,就可以把 session 都存到一個庫里,
  • 二是從「分布」角度,讓相同 IP 的請求在負載均衡時都打到同一臺機器上,以 nginx 為例,可以配置 ip_hash 來實作,

但通常還是采用第一種方式,因為第二種相當于閹割了負載均衡,且仍沒有解決「用戶請求的機器宕機」的問題,「node.js 下的 session 處理」 前面的圖很清楚了,服務端要實作對 cookie 和 session 的存取,實作起來要做的事還是很多的,在npm中,已經有封裝好的中間件,比如 express-session - npm,用法就不貼了,這是它種的 cookie:

express-session - npm 主要實作了:

  • 封裝了對cookie的讀寫操作,并提供配置項配置欄位、加密方式、過期時間等,
  • 封裝了對session的存取操作,并提供配置項配置session存盤方式(記憶體/redis)、存盤規則等,
  • 給req提供了session屬性,控制屬性的set/get并回應到cookie和session存取上,并給req.session提供了一些方法,

應用方案:token

session 的維護給服務端造成很大困擾,我們必須找地方存放它,又要考慮分布式的問題,甚至要單獨為了它啟用一套 Redis 集群,有沒有更好的辦法?

我又想到學校,在沒有校園卡技術以前,我們都靠「學生證」,門衛小哥直接對照我和學生證上的臉,確認學生證有效期、年級等資訊,就可以放行了,

回過頭來想想,一個登錄場景,也不必往 session 存太多東西,那為什么不直接打包到 cookie 中呢?這樣服務端不用存了,每次只要核驗 cookie 帶的「證件」有效性就可以了,也可以攜帶一些輕量的資訊,這種方式通常被叫做 token,

token 的流程是這樣的:

  • 用戶登錄,服務端校驗賬號密碼,獲得用戶資訊
  • 把用戶資訊、token 配置編碼成 token,通過 cookie set 到瀏覽器
  • 此后用戶請求業務介面,通過 cookie 攜帶 token
  • 介面校驗 token 有效性,進行正常業務介面處理

「客戶端 token 的存盤方式」 在前面 cookie 說過,cookie 并不是客戶端存盤憑證的唯一方式,token 因為它的「無狀態性」,有效期、使用限制都包在 token 內容里,對 cookie 的管理能力依賴較小,客戶端存起來就顯得更自由,但 web 應用的主流方式仍是放在 cookie 里,畢竟少操心, 「token 的過期」 那我們如何控制 token 的有效期呢?很簡單,把「過期時間」和資料一起塞進去,驗證時判斷就好,

token 的編碼

編碼的方式豐儉由人,「base64」 比如 node 端的 cookie-session - npm 庫

不要糾結名字,其實是個 token 庫,但保持了和 express-session - npm 高度一致的用法,把要存的資料掛在 session 上

默認配置下,當我給他一個 userid,他會存成這樣:

這里的 eyJ1c2VyaWQiOiJhIn0=,就是 {"userid":"abb”} 的 base64 而已,「防篡改」

那問題來了,如果用戶 cdd 拿{"userid":"abb”}轉了個 base64,再手動修改了自己的 token 為 eyJ1c2VyaWQiOiJhIn0=,是不是就能直接訪問到 abb 的資料了?

是的,所以看情況,如果 token 涉及到敏感權限,就要想辦法避免 token 被篡改,解決方案就是給 token 加簽名,來識別 token 是否被篡改過,例如在 cookie-session - npm 庫中,增加兩項配置:

secret: 'iAmSecret',signed: true,

這樣會多種一個 .sig cookie,里面的值就是 {"userid":"abb”}iAmSecret通過加密演算法計算出來的,常見的比如HMACSHA256 類 (System.Security.Cryptography) | Microsoft Docs,

好了,現在 cdd 雖然能偽造出eyJ1c2VyaWQiOiJhIn0=,但偽造不出 sig 的內容,因為他不知道 secret,「JWT」 但上面的做法額外增加了 cookie 數量,資料本身也沒有規范的格式,所以 JSON Web Token Introduction - jwt.io 橫空出世了,

JSON Web Token (JWT) 是一個開放標準,定義了一種傳遞 JSON 資訊的方式,這些資訊通過數字簽名確保可信,

它是一種成熟的 token 字串生成方案,包含了我們前面提到的資料、簽名,不如直接看一下一個 JWT token 長什么樣:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyaWQiOiJhIiwiaWF0IjoxNTUxOTUxOTk4fQ.2jf3kl_uKWRkwjOP6uQRJFqMlwSABcgqqcJofFH5XCo

這串東西是怎么生成的呢?看圖:

型別、加密演算法的選項,以及 JWT 標準資料欄位,可以參考 RFC 7519 - JSON Web Token (JWT)node 上同樣有相關的庫實作:express-jwt - npm koa-jwt - npm

refresh token

token,作為權限守護者,最重要的就是「安全」,業務介面用來鑒權的 token,我們稱之為 access token,越是權限敏感的業務,我們越希望 access token 有效期足夠短,以避免被盜用,但過短的有效期會造成 access token 經常過期,過期后怎么辦呢?一種辦法是,讓用戶重新登錄獲取新 token,顯然不夠友好,要知道有的 access token 過期時間可能只有幾分鐘,另外一種辦法是,再來一個 token,一個專門生成 access token 的 token,我們稱為 refresh token,

  • access token 用來訪問業務介面,由于有效期足夠短,盜用風險小,也可以使請求方式更寬松靈活
  • refresh token 用來獲取 access token,有效期可以長一些,通過獨立服務和嚴格的請求方式增加安全性;由于不常驗證,也可以如前面的 session 一樣處理

有了 refresh token 后,幾種情況的請求流程變成這樣:

如果 refresh token 也過期了,就只能重新登錄了,

session 和 token

session 和 token 都是邊界很模糊的概念,就像前面說的,refresh token 也可能以 session 的形式組織維護,狹義上,我們通常認為 session 是「種在 cookie 上、資料存在服務端」的認證方案,token 是「客戶端存哪都行、資料存在 token 里」的認證方案,對 session 和 token 的對比本質上是「客戶端存 cookie / 存別地兒」、「服務端存資料 / 不存資料」的對比,「客戶端存 cookie / 存別地兒」 存 cookie 固然方便不操心,但問題也很明顯:

  • 在瀏覽器端,可以用 cookie(實際上 token 就常用 cookie),但出了瀏覽器端,沒有 cookie 怎么辦?
  • cookie 是瀏覽器在域下自動攜帶的,這就容易引發 CSRF 攻擊(前端安全系列(二):如何防止CSRF攻擊?- 美團技術團隊)

存別的地方,可以解決沒有 cookie 的場景;通過引數等方式手動帶,可以避免 CSRF 攻擊,「服務端存資料 / 不存資料」

  • 存資料:請求只需攜帶 id,可以大幅縮短認證字串長度,減小請求體積
  • 不存資料:不需要服務端整套的解決方案和分布式處理,降低硬體成本;避免查庫帶來的驗證延遲

單點登錄

前面我們已經知道了,在同域下的客戶端/服務端認證系統中,通過客戶端攜帶憑證,維持一段時間內的登錄狀態,但當我們業務線越來越多,就會有更多業務系統分散到不同域名下,就需要「一次登錄,全線通用」的能力,叫做「單點登錄」,

“虛假”的單點登錄(主域名相同)

簡單的,如果業務系統都在同一主域名下,比如wenku.baidu.com tieba.baidu.com,就好辦了,可以直接把 cookie domain 設定為主域名 baidu.com,百度也就是這么干的,

“真實”的單點登錄(主域名不同)

比如滴滴這么潮的公司,同時擁有didichuxing.com xiaojukeji.com didiglobal.com等域名,種 cookie 是完全繞不開的,這要能實作「一次登錄,全線通用」,才是真正的單點登錄,這種場景下,我們需要獨立的認證服務,通常被稱為 SSO,「一次「從 A 系統引發登錄,到 B 系統不用登錄」的完整流程」

  • 用戶進入 A 系統,沒有登錄憑證(ticket),A 系統給他跳到 SSO
  • SSO 沒登錄過,也就沒有 sso 系統下沒有憑證(注意這個和前面 A ticket 是兩回事),輸入賬號密碼登錄
  • SSO 賬號密碼驗證成功,通過介面回傳做兩件事:一是種下 sso 系統下憑證(記錄用戶在 SSO 登錄狀態);二是下發一個 ticket
  • 客戶端拿到 ticket,保存起來,帶著請求系統 A 介面
  • 系統 A 校驗 ticket,成功后正常處理業務請求
  • 此時用戶第一次進入系統 B,沒有登錄憑證(ticket),B 系統給他跳到 SSO
  • SSO 登錄過,系統下有憑證,不用再次登錄,只需要下發 ticket
  • 客戶端拿到 ticket,保存起來,帶著請求系統 B 介面

「完整版本:考慮瀏覽器的場景」 上面的程序看起來沒問題,實際上很多 APP 等端上這樣就夠了,但在瀏覽器下不見得好用,看這里:

對瀏覽器來說,SSO 域下回傳的資料要怎么存,才能在訪問 A 的時候帶上?瀏覽器對跨域有嚴格限制,cookie、localStorage 等方式都是有域限制的,這就需要也只能由 A 提供 A 域下存盤憑證的能力,一般我們是這么做的:

圖中我們通過顏色把瀏覽器當前所處的域名標記出來,注意圖中灰底文字說明部分的變化,

  • 在 SSO 域下,SSO 不是通過介面把 ticket 直接回傳,而是通過一個帶 code 的 URL 重定向到系統 A 的介面上,這個介面通常在 A 向 SSO 注冊時約定
  • 瀏覽器被重定向到 A 域下,帶著 code 訪問了 A 的 callback 介面,callback 介面通過 code 換取 ticket
  • 這個 code 不同于 ticket,code 是一次性的,暴露在 URL 中,只為了傳一下換 ticket,換完就失效
  • callback 介面拿到 ticket 后,在自己的域下 set cookie 成功
  • 在后續請求中,只需要把 cookie 中的 ticket 決議出來,去 SSO 驗證就好
  • 訪問 B 系統也是一樣

總結

  • HTTP 是無狀態的,為了維持前后請求,需要前端存盤標記
  • cookie 是一種完善的標記方式,通過 HTTP 頭或 js 操作,有對應的安全策略,是大多數狀態管理方案的基石
  • session 是一種狀態管理方案,前端通過 cookie 存盤 id,后端存盤資料,但后端要處理分布式問題
  • token 是另一種狀態管理方案,相比于 session 不需要后端存盤,資料全部存在前端,解放后端,釋放靈活性
  • token 的編碼技術,通常基于 base64,或增加加密演算法防篡改,jwt 是一種成熟的編碼方案
  • 在復雜系統中,token 可通過 service token、refresh token 的分權,同時滿足安全性和用戶體驗
  • session 和 token 的對比就是「用不用cookie」和「后端存不存」的對比
  • 單點登錄要求不同域下的系統「一次登錄,全線通用」,通常由獨立的 SSO 系統記錄登錄狀態、下發 ticket,各業務系統配合存盤和認證 ticket

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了,,,

3.Spring Boot 2.x 教程,太全了!

4.Spring Boot 2.6 正式發布,一大波新特性,,

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

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

標籤:Java

上一篇:Ubuntu16桌面版編譯OpenCV4的java庫和so庫

下一篇:Java精品視瞥澩,自學Java人手一套!

標籤雲
其他(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)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more