摘要:頭部需要一個頭部,用于描述關于該的最基本的信息,例如其類型以及簽名所用的算法等。簽發者需要準備一個可以確認自己身份的字符串,這個字符串我們稱之為。
什么是 jwt ?
JWT 全稱叫 JSON Web Token, 是一個非常輕巧的規范。這個規范允許我們使用 JWT 在用戶和服務器之間傳遞安全可靠的信息。
jwt 使用場景jwt 用圖廣泛,例如授權、鑒權等。具體一點的話,假如我們有一個 A 用戶想要邀請某用戶進入自己的群組,此時 A 用戶需要生成一條邀請鏈接,鏈接內容大致如下: https://host/group/{group_id}/invite/{invite_user}
此時這個鏈接點擊進去雖然可以實現讓用戶加入群組,但是用戶可以隨意更改這個鏈接的參數,例如改改 group 后面的ID,從而加入其他任意群組,改改 invite 后面的邀請人等等操作。所以這種 URL 并不是安全的,那么這種情況下,我們就可以使用 jwt 來實創建一個安全的邀請鏈接了。
首先 URL 要簡單改一下, https://host/group/invite/{token}
可以看到我們去掉了 groupId 和 inviteUser 參數,添加了一個 token 參數,可想而知, groupId 和 inviteUser 應該是被包含進 token 里面了,如何實現這個看似很神奇的 token 呢? 我們來看看 jwt 的原理吧。
在講 jwt 原理之前得先知道 jwt 由哪些東西組成。
jwt 組成一個 JWT 實際上就是一個字符串,它由三部分組成,頭部、載荷與簽名。
頭部 (Header)JWT 需要一個頭部,用于描述關于該 JWT 的最基本的信息,例如其類型以及簽名所用的算法等。這也可以被表示成一個 JSON 對象,如:
{ "typ": "JWT", "alg": "md5" }
將上面的 json 字符串使用 base64 進行編碼后,可以得到一下內容,我們稱其為 JWT 的頭部(Header)。
eyJ0eXAiOiJqd3QiLCJhbGciOiJtZDUifQ==載荷(Payload)
我們先將上面的邀請入群的操作描述成一個 JSON 對象。其中添加了一些其他的信息,幫助今后收到這個 JWT 的服務器理解這個JWT。
{ "sub": "1", "iss": "http://host/group/invite", "iat": 1451888119, "exp": 1454516119, "nbf": 1451888119, "jti": "37c107e4609ddbcc9c096ea5ee76c667", "group_id": 1, "invite_user": "A" }
這里面的前6個字段都是由JWT的標準所定義的。
sub: 該 JWT 所面向的用戶
iss: 該 JWT 的簽發者
iat(issued at): 在什么時候簽發的 token
exp(expires): token 什么時候過期
nbf(not before):token 在此時間之前不能被接收處理
jti:JWT ID為web token 提供唯一標識
將上面的 json 字符串使用 base64 進行編碼后,可以得到一下內容,我們稱其為 JWT 的載荷(Payload)。
eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvOiIsImV4cCI6MTUyNzY2NzY2MywiaWF0IjoxNTI3NjY0MDYzLCJuYmYiOjE1Mjc2NjQwNjMsImdyb3VwX2lkIjoxLCJpbnZpdGVfdXNlciI6IkEiLCJqdGkiOiJlMjE4ZTJhZDdlYTdmZjUzYTVhM2RlZjA0MmFjMjM4NCJ9簽名(Signature)
在簽名之前我們需要先得到用于簽名的字符串, 將頭部和載荷使用 . 進行拼接(頭部在前), 得到用于簽名的字符串
eyJ0eXAiOiJqd3QiLCJhbGciOiJtZDUifQ==.eyJzdWIiOiIxIiwiaXNzIjoiaHR0cDpcL1wvOiIsImV4cCI6MTUyNzY2NzY2MywiaWF0IjoxNTI3NjY0MDYzLCJuYmYiOjE1Mjc2NjQwNjMsImdyb3VwX2lkIjoxLCJpbnZpdGVfdXNlciI6IkEiLCJqdGkiOiJlMjE4ZTJhZDdlYTdmZjUzYTVhM2RlZjA0MmFjMjM4NCJ9
然后使用簽名方法對用于簽名的字符串進行簽名, 得到如下字符串,即 簽名(Signature)
NDljMzljOTkyOGNmYWU1NGEyZDYzMTk5NTNlNGEwZDA=
最后把用于簽名的字符串和簽名使用 . 進行拼接(簽名在后), 即可得到 一個完整的 token。但是,此時的
token 沒有帶上簽發者特有的標志,是可以被偽造的,至于如何解決這個問題我們下面 jwt 具體實現會講。
上面說完 jwt 組成,相信你已經知道 jwt 大概是個啥子東西了 --- 就是一個字符串!!!
那么這個字符串如何保證不被篡改呢 ? 這里就要引入 secret 了。
回到上面的例子,邀請用戶入群這個場景,雖然我們上面把 參數改成了 token 這種形式,但是你可能會發現,這樣的 token 別人捕獲了之后,任然可以自己偽造一個類似的 token ,因為此時的簽名(Signature)并沒有簽發者特有的身份信息,所有數據都是明文的,所以這樣簽名是不安全的,應該加上 secret 進行簽名。
簽發者需要準備一個可以確認自己身份的字符串,這個字符串我們稱之為 secret 。以 md5 作為簽名方法為例(并不建議使用 md5 作為簽名方法),我們只需要將上面準備的 用于簽名的字符串簡單的與 secret 進行拼接,然后進行 md5 計算,這時候得到的簽名是受 secret 值影響的,所以即便他人捕獲了之后 token,他仍然不能隨意篡改 token 的內容,因為他不知道 secret 和拼接方法,故此時的 token 是安全的,不可被惡意篡改的。
$signatureString = "pen"; // 原始數據 $secret = "apple"; // 簽發者 secret $originSignature = md5($signatureString ."-". $secret); print_r($signature); // apple-pen $signatureString = "pen"; // 原始數據 $secret = "pineapple"; // 不一樣的 secret $fakeSignature = md5($signatureString ."-". $secret); print_r($signature); // pineapple-pen // 可以看到不一樣的 secret 會生成完全不一樣的簽名,這樣我們的數據就可以保證不能被隨意篡改了~jwt 傳輸的數據會泄露 ?
是的,jwt 的頭部和載荷字段都可以被解碼(base64 屬于編碼,是可以被解碼的)。所以并不建議用 jwt 傳輸敏感信息,例如密碼,因為這很容易被捕獲后解碼,從而被竊取。
secret 一個字符串不足以描述簽發者信息 ?我們可以將 簽發者信息描述成一個 json ,然后對這個 json 字符串進行編碼,這樣同樣可以得到一個 secret 字符串。
jwt 實現先來一個最粗暴的 jwt 實現
最簡單粗暴的 jwt for php 實現class JWT { protected $headers; protected $payload; /** * @return array */ public function getHeaders(): array { return $this->headers; } /** * @return array */ public function getPayload(): array { return $this->payload; } public function __construct(array $headers, array $payload) { $this->setHeaders($headers); $this->setPayload($payload); } public function setHeaders(array $headers): void { $this->headers = $headers; } public function setPayload(array $payload): void { $this->payload = $payload; } /** * 獲取用于簽名的字符串 * * @return string */ public function signatureStr(): string { $headersStr = $this::encodeStr(json_encode($this->headers)); $payloadStr = $this::encodeStr(json_encode($this->payload)); return "{$headersStr}.{$payloadStr}"; } /** * 編碼 * * @param string $string * * @return string */ protected static function encodeStr(string $string): string { return rtrim(strtr(base64_encode($string), "+/", "-_"), "="); } /** * 解碼 * * @param string $string * * @return string */ protected static function decodeStr(string $string): string { return base64_decode(strtr($string, "-_", "+/")); } /** * 簽名,此時的 secret 為 qbhy * * @param string $string * * @return string */ protected static function signature(string $string): string { return md5($string . "qbhy"); } /** * 校驗簽名 * * @param string $signStr * @param string $sign * * @return bool */ protected static function checkSignature(string $signStr, string $sign): bool { return static::signature($signStr) === $sign; } /** * 生成 token * * @return string */ public function token(): string { $signStr = $this->signatureStr(); $token = $signStr . "." . $this::signature($signStr); return $token; } /** * 從 token 中獲取數據 * * @param string $token * * @return AppModulesJWTJWT * @throws AppModulesJWTJWTException */ public static function fromToken(string $token): JWT { $arr = explode(".", $token); if (count($arr) !== 3) { throw new JWTException("token 錯誤"); } if (!static::checkSignature("{$arr[0]}.{$arr[1]}", $arr[2])) { throw new JWTException("簽名錯誤"); } $headers = json_decode(static::decodeStr($arr[0]), true); $payload = json_decode(static::decodeStr($arr[1]), true); return new static($headers, $payload); } }simple-jwt
這里先安利一下我寫的一個基于 php 的 jwt 擴展包 --- 96qbhy/simple-jwt , 這個包實現了完整的 jwt 規范,開箱即用,你可以基于 96qbhy/simple-jwt 來給你的應用添加 jwt 相關功能。
我把 simple-jwt 拆分成,Encoder(編碼器) 、Encrypter(簽名器) 、JWT、JWTManager 四部分,你可以自行擴展 Encoder、Encrypter,從而實現自己的編碼和加密方法,感興趣的同學可以去 github 看看源碼 96qbhy/simple-jwt 。有問題歡迎與我討論,同時歡迎 Issue 和 PR 。
如有錯誤歡迎指出,謝謝。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28719.html
摘要:客戶端發起非登錄請求時,服務端通過中的找到對應的來知道此次請求是誰發出的。數量隨著登錄用戶的增多而增多,存儲會增加很多。還記得在上家公司做全干工程師的時候,基本從頁面寫到運維,當時做登錄這塊的時候,被session、cookie、token各種概念差點整蒙圈了,上網查詢相關概念,發現很多人都是類似的疑惑,比如:showImg(https://user-gold-cdn.xitu.io/201...
摘要:要對進行黑盒測試測試的最好辦法是對他們進行黑盒測試,黑盒測試是一種不關心應用內部結構和工作原理的測試方法,測試時系統任何部分都不應該被。此外,有了黑盒測試并不意味著不需要單元測試,針對的單元測試還是需要編寫的。 本文首發于之乎專欄前端周刊,全文共 6953 字,讀完需 8 分鐘,速度需 2 分鐘。翻譯自:RingStack 的文章 https://blog.risingstack.co...
摘要:微服務架構著重培養通用可重用的服務。服務注冊和發現微服務架構下,有大量的微服務需要處理。網關也是獲得微服務狀態監控信息的中心。實際情況是,微服務和其它企業架構并存。 引言:上篇文章介紹了微服務和單體架構的區別、微服務的設計、消息、服務間通信、數據去中心化,本篇會繼續深入微服務,介紹其它特性。 治理去中心化 通常治理的意思是構建方案,并且迫使人們通過努力達到組織的目標。SOA治理指導開發...
摘要:微服務架構著重培養通用可重用的服務。服務注冊和發現微服務架構下,有大量的微服務需要處理。網關也是獲得微服務狀態監控信息的中心。實際情況是,微服務和其它企業架構并存。 引言:上篇文章介紹了微服務和單體架構的區別、微服務的設計、消息、服務間通信、數據去中心化,本篇會繼續深入微服務,介紹其它特性。 治理去中心化 通常治理的意思是構建方案,并且迫使人們通過努力達到組織的目標。SOA治理指導開發...
閱讀 3077·2021-09-22 15:20
閱讀 2600·2019-08-30 15:54
閱讀 1966·2019-08-30 14:06
閱讀 3114·2019-08-30 13:05
閱讀 2457·2019-08-29 18:36
閱讀 568·2019-08-29 15:10
閱讀 523·2019-08-29 11:17
閱讀 818·2019-08-28 18:11