應用程序身份驗證曾經只依賴於用戶名/郵箱和密碼等憑據,會話用於維護用戶狀態直至用戶註銷。之後,我們開始使用身份驗證API。最近,JSON Web Tokens (JWT) 越來越多地用於對服務器請求進行身份驗證。
本文將介紹JWT是什麼以及如何使用PHP進行基於JWT的用戶請求身份驗證。
要點
JWT與會話
首先,為什麼會話不是那麼好呢?主要有三個原因:
JWT
現在,讓我們開始學習JWT。 JSON Web Token規範(RFC 7519)於2010年12月28日首次發布,最近一次更新是在2015年5月。
JWT比API密鑰具有許多優勢,包括:
JWT長什麼樣?
這是一個JWT示例:
<code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MTY5MjkxMDksImp0aSI6ImFhN2Y4ZDBhOTVjIiwic2NvcGVzIjpbInJlcG8iLCJwdWJsaWNfcmVwbyJdfQ.XCEwpBGvOLma4TCoh36FU7XhUbcskygS81HE1uHLf0E</code>
乍一看,這個字符串似乎只是用句點或點字符連接的隨機字符組。因此,它似乎與API密鑰沒有什麼不同。但是,如果您仔細觀察,就會發現有三個單獨的字符串。
第一個字符串是JWT標頭。它是一個Base64 URL編碼的JSON字符串。它指定了用於生成簽名的加密算法以及令牌的類型,該類型始終設置為JWT。該算法可以是對稱的或非對稱的。
對稱算法使用單個密鑰來創建和驗證令牌。該密鑰在JWT的創建者和使用者之間共享。務必確保只有創建者和使用者知道密鑰。否則,任何人都可以創建有效的令牌。
非對稱算法使用私鑰來簽署令牌,並使用公鑰來驗證令牌。當共享密鑰不切實際或其他方只需要驗證令牌的完整性時,應使用這些算法。
第二個字符串是JWT的有效負載。它也是一個Base64 URL編碼的JSON字符串。它包含一些標準字段,稱為“聲明”。聲明有三種類型:註冊的、公共的和私有的。
註冊的聲明是預定義的。您可以在JWT的RFC中找到它們的列表。以下是一些常用的聲明:
您可以根據需要定義公共聲明。但是,它們不能與註冊的聲明或已存在的公共聲明的聲明相同。您可以隨意創建私有聲明。它們僅供雙方使用:生產者和消費者。
JWT的簽名是一種加密機制,旨在使用對令牌內容唯一的數字簽名來保護JWT的數據。簽名確保JWT的完整性,以便使用者可以驗證它沒有被惡意行為者篡改。
JWT的簽名是三件事的組合:
這三者使用JWT標頭中指定的算法進行數字簽名(未加密)。如果我們解碼上面的示例,我們將得到以下JSON字符串:
JWT的標頭
<code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MTY5MjkxMDksImp0aSI6ImFhN2Y4ZDBhOTVjIiwic2NvcGVzIjpbInJlcG8iLCJwdWJsaWNfcmVwbyJdfQ.XCEwpBGvOLma4TCoh36FU7XhUbcskygS81HE1uHLf0E</code>
JWT的數據
<code>{ "alg": "HS256", "typ": "JWT" }</code>
您可以自己嘗試jwt.io,在那裡您可以嘗試編碼和解碼您自己的JWT。
在基於PHP的應用程序中使用JWT
既然您已經了解了JWT是什麼,那麼現在是時候學習如何在PHP應用程序中使用它們了。在深入研究之前,您可以隨意克隆本文的代碼,或者按照我們的步驟進行操作。
您可以採用多種方法來集成JWT,但以下是我們將要採用的方法。
除登錄和註銷頁面外,對應用程序的所有請求都需要通過JWT進行身份驗證。如果用戶在沒有JWT的情況下發出請求,他們將被重定向到登錄頁面。
用戶填寫並提交登錄表單後,表單將通過JavaScript提交到我們應用程序中的登錄端點authenticate.php。然後,端點將從請求中提取憑據(用戶名和密碼),並檢查它們是否有效。
如果有效,它將生成一個JWT並將其發送回客戶端。當客戶端收到JWT時,它將存儲JWT並將其用於對應用程序的未來每次請求。
對於一個簡單的場景,用戶只能請求一個資源——一個恰當命名的PHP文件resource.php。它不會做太多事情,只是返回一個包含請求時當前時間戳的字符串。
在發出請求時,可以使用多種方法來使用JWT。在我們的應用程序中,JWT將發送在Bearer授權標頭中。
如果您不熟悉Bearer Authorization,它是一種HTTP身份驗證形式,其中令牌(例如JWT)發送在請求標頭中。服務器可以檢查令牌並確定是否應授予令牌的“持有者”訪問權限。
這是一個標頭的示例:
<code>{ "iat": 1416929109, "jti": "aa7f8d0a95c", "scopes": [ "repo", "public_repo" ] }</code>
對於我們的應用程序收到的每個請求,PHP都將嘗試從Bearer標頭中提取令牌。如果存在,則對其進行驗證。如果有效,用戶將看到該請求的正常響應。但是,如果JWT無效,則不允許用戶訪問資源。
請注意,JWT並非旨在替代會話cookie。
首先,我們需要在我們的系統上安裝PHP和Composer。
在項目的根目錄中,運行composer install。這將引入Firebase PHP-JWT,這是一個簡化JWT操作的第三方庫,以及用於簡化應用程序中對配置數據訪問的laminas-config。
安裝庫後,讓我們逐步完成authenticate.php中的登錄代碼。我們首先進行通常的設置,確保Composer生成的自動加載器可用。
<code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MTY5MjkxMDksImp0aSI6ImFhN2Y4ZDBhOTVjIiwic2NvcGVzIjpbInJlcG8iLCJwdWJsaWNfcmVwbyJdfQ.XCEwpBGvOLma4TCoh36FU7XhUbcskygS81HE1uHLf0E</code>
收到表單提交後,憑據將針對數據庫或其他一些數據存儲進行驗證。出於本示例的目的,我們將假設它們有效,並將$hasValidCredentials設置為true。
<code>{ "alg": "HS256", "typ": "JWT" }</code>
接下來,我們初始化一組變量,用於生成JWT。請記住,由於JWT可以在客戶端進行檢查,因此不要在其中包含任何敏感信息。
另一件值得指出的是,$secretKey不會像這樣初始化。您可能會在環境中設置它並提取它,使用phpdotenv等庫,或在配置文件中設置它。在本例中,我避免這樣做,因為我想關注JWT代碼。
切勿洩露它或將其存儲在版本控制下!
<code>{ "iat": 1416929109, "jti": "aa7f8d0a95c", "scopes": [ "repo", "public_repo" ] }</code>
準備好有效負載數據後,我們接下來使用php-jwt的靜態encode方法來創建JWT。
該方法:
它接受三個參數:
通過對函數結果調用echo,返回生成的令牌:
<code>Authorization: Bearer ab0dde18155a43ee83edba4a4542b973</code>
現在客戶端有了令牌,您可以使用JavaScript或您喜歡的任何機制來存儲它。以下是如何使用原生JavaScript進行操作的示例。在index.html中,成功提交表單後,收到的JWT將存儲在內存中,登錄表單將被隱藏,並且將顯示請求時間戳的按鈕:
<code class="language-php"><?php declare(strict_types=1); use Firebase\JWT\JWT; require_once('../vendor/autoload.php');</code>
單擊“獲取當前時間戳”按鈕時,將向resource.php發出GET請求,該請求在Authorization標頭中設置身份驗證後收到的JWT。
<code class="language-php"><?php // 从请求中提取凭据 if ($hasValidCredentials) {</code>
當我們單擊按鈕時,將發出類似於以下內容的請求:
<code class="language-php">$secretKey = 'bGS6lzFqvvSQ8ALbOxatm7/Vk7mLQyzqaS34Q4oR1ew='; $issuedAt = new DateTimeImmutable(); $expire = $issuedAt->modify('+6 minutes')->getTimestamp(); // 添加60秒 $serverName = "your.domain.name"; $username = "username"; // 从过滤后的POST数据中检索 $data = [ 'iat' => $issuedAt->getTimestamp(), // 颁发时间:生成令牌的时间 'iss' => $serverName, // 颁发者 'nbf' => $issuedAt->getTimestamp(), // 不早于 'exp' => $expire, // 过期 'userName' => $username, // 用户名 ];</code>
假設JWT有效,我們將看到資源,之後響應將寫入控制台。
最後,讓我們看看如何在PHP中驗證令牌。與往常一樣,我們將包含Composer的自動加載器。然後,我們可以選擇檢查是否使用了正確的請求方法。為了繼續關注JWT特定的代碼,我已經跳過了執行此操作的代碼:
<code class="language-php"><?php // 将数组编码为JWT字符串。 echo JWT::encode( $data, $secretKey, 'HS512' ); }</code>
然後,代碼將嘗試從Bearer標頭中提取令牌。我已經使用preg_match這樣做了。如果您不熟悉該函數,它將在字符串上執行正則表達式匹配。
我在這裡使用的正則表達式將嘗試從Bearer標頭中提取令牌,並轉儲其他所有內容。如果找不到,則返回HTTP 400錯誤請求:
<code class="language-javascript">const store = {}; const loginButton = document.querySelector('#frmLogin'); const btnGetResource = document.querySelector('#btnGetResource'); const form = document.forms[0]; // 将jwt插入到store对象中 store.setJWT = function (data) { this.JWT = data; }; loginButton.addEventListener('submit', async (e) => { e.preventDefault(); const res = await fetch('/authenticate.php', { method: 'POST', headers: { 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: JSON.stringify({ username: form.inputEmail.value, password: form.inputPassword.value }) }); if (res.status >= 200 && res.status < 300) { const jwt = await res.text(); store.setJWT(jwt); frmLogin.style.display = 'none'; btnGetResource.style.display = 'block'; } else { // 处理错误 console.log(res.status, res.statusText); } });</code>
請注意,默認情況下,Apache不會將HTTP_AUTHORIZATION標頭傳遞給PHP。其背後的原因是:
基本授權標頭只有在您的連接通過HTTPS完成時才安全,因為否則憑據將以編碼的明文(未加密)形式通過網絡發送,這是一個巨大的安全問題。
我完全理解這一決定的邏輯。但是,為了避免很多混淆,請將以下內容添加到您的Apache配置中。然後代碼將按預期工作。如果您使用的是NGINX,則代碼應該按預期工作:
<code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MTY5MjkxMDksImp0aSI6ImFhN2Y4ZDBhOTVjIiwic2NvcGVzIjpbInJlcG8iLCJwdWJsaWNfcmVwbyJdfQ.XCEwpBGvOLma4TCoh36FU7XhUbcskygS81HE1uHLf0E</code>
接下來,我們嘗試提取匹配的JWT,它將位於$matches變量的第二個元素中。如果不可用,則沒有提取JWT,並返回HTTP 400錯誤請求:
<code>{ "alg": "HS256", "typ": "JWT" }</code>
如果我們到達此點,則已提取JWT,因此我們轉到解碼和驗證階段。為此,我們再次需要我們的密鑰,它將從環境或應用程序的配置中提取。然後,我們使用php-jwt的靜態decode方法,將JWT、密鑰和一組用於解碼JWT的算法傳遞給它。
如果能夠成功解碼,我們就會嘗試驗證它。我這裡的示例非常簡單,因為它只使用頒發者、不早於和過期時間戳。在實際應用程序中,您可能還會使用許多其他聲明。
<code>{ "iat": 1416929109, "jti": "aa7f8d0a95c", "scopes": [ "repo", "public_repo" ] }</code>
如果令牌無效,例如令牌已過期,則用戶將收到HTTP 401未授權標頭,並且腳本將退出。
如果解碼和驗證過程失敗,則可能是:
如您所見,JWT具有一套不錯的控制措施,無需手動撤銷或針對有效令牌列表進行檢查即可將其標記為無效。
如果解碼和驗證過程成功,則用戶將被允許發出請求,並將收到相應的響應。
總結
這是一個關於JSON Web Tokens(或JWT)以及如何在基於PHP的應用程序中使用它們的快速介紹。從這裡開始,您可以嘗試在下一個API中實現JWT,也許嘗試一些使用非對稱密鑰(如RS256)的其他簽名算法,或者將其集成到現有的OAUTH2身份驗證服務器中以用作API密鑰。
如果您有任何意見或問題,請隨時通過Twitter與我們聯繫。
關於使用JWT進行PHP授權的常見問題解答
您確實可以在PHP中使用JWT來在Web應用程序中建立身份驗證和授權機制。要開始使用,您需要使用Composer安裝PHP JWT庫,例如“firebase/php-jwt”或“lcobucci/jwt”。這些庫提供了創建、編碼、解碼和驗證JWT的必要工具。 要創建JWT,您可以使用庫來構建包含發行者、受眾、過期時間等聲明的令牌。創建後,您可以使用密鑰簽署令牌。接收JWT時,您可以使用庫對其進行解碼和驗證以確保其真實性。如果令牌有效且已驗證,您可以訪問其聲明以確定用戶身份和權限,從而允許您在PHP應用程序中實現安全的身份驗證和授權。 保護密鑰並遵循使用JWT時的安全最佳實踐對於防止未經授權訪問應用程序資源至關重要。
PHP中的JWT(JSON Web Token)身份驗證是一種廣泛用於在Web應用程序中實現用戶身份驗證和授權的方法。它基於令牌的身份驗證,能夠進行安全且無狀態的用戶驗證。以下是JWT身份驗證在PHP中的工作方式: 首先,在用戶身份驗證或登錄期間,服務器會生成一個JWT,這是一個緊湊的、自包含的令牌,其中包含與用戶相關的信息(聲明),例如用戶ID、用戶名和角色。這些聲明通常是JSON數據。然後,服務器使用密鑰簽署此令牌以確保其完整性和真實性。 其次,成功進行身份驗證後,服務器會將JWT發送回客戶端,客戶端通常將其存儲在安全位置,例如HTTP cookie或本地存儲。此令牌作為身份驗證的證明。 最後,對於對服務器上受保護資源的後續請求,客戶端會在請求標頭中附加JWT,通常使用帶有“Bearer”方案的“Authorization”標頭。服務器收到JWT後,會使用在令牌創建期間使用的相同密鑰來驗證其簽名。此外,它還會檢查令牌是否未過期且包含有效的聲明。成功驗證後,它會從令牌聲明中提取用戶信息,並實現授權邏輯以確保用戶具有訪問請求資源所需的權限。這種方法允許在PHP應用程序中進行安全、無狀態的身份驗證,而無需服務器端會話存儲。 雖然PHP中的JWT身份驗證提供了許多好處,例如可擴展性和無狀態性,但保護密鑰並採用令牌管理的最佳實踐對於維護應用程序的安全至關重要。使用已建立的PHP JWT庫可以簡化令牌處理並增強安全性。
PHP中用於身份驗證和授權的JWT(JSON Web Tokens)的替代方案是基於會話的身份驗證。在基於會話的身份驗證中,服務器為每個已驗證的用戶維護一個會話。當用戶登錄時,會創建一個唯一的會話標識符(通常存儲為客戶端瀏覽器上的會話cookie)。此標識符用於將用戶與服務器端會話數據關聯起來,包括與用戶相關的信息,例如用戶ID、用戶名和權限。 基於會話的身份驗證提供簡單性和易於實現,使其適用於各種Web應用程序,尤其是在您不需要JWT的無狀態性和可擴展性功能時。它本質上是有狀態的,當您需要在用戶會話期間管理特定於用戶的數據(例如購物車內容或用戶首選項)時,這可能是有利的。 但是,使用基於會話的身份驗證時,需要考慮一些事項。對於需要無狀態身份驗證的分佈式或基於微服務的架構,它可能不太適用。此外,管理用戶會話可能會增加服務器負載,尤其是在用戶群較大的應用程序中。最終,在PHP中選擇JWT和基於會話的身份驗證應與應用程序的特定需求和設計考慮因素相符,確保安全有效的身份驗證機制最能滿足您的需求。
使用JWT(JSON Web Tokens)保護PHP API涉及一個多步驟過程,該過程結合了身份驗證和授權。首先,選擇合適的PHP JWT庫(如“firebase/php-jwt”或“lcobucci/jwt”)來處理與令牌相關的操作,並使用Composer管理依賴項。 對於身份驗證,您需要在PHP應用程序中實現用戶身份驗證系統。此系統會針對您的數據庫或身份驗證提供程序驗證用戶憑據。成功進行身份驗證後,您將生成一個JWT令牌,其中包含與用戶相關的聲明,例如用戶的ID、用戶名和角色。務必設置過期時間以控制令牌的有效性,然後使用密鑰簽署令牌。此已簽名的令牌作為身份驗證響應的一部分發送回客戶端。 客戶端安全地存儲收到的JWT,通常存儲在HTTP cookie或本地存儲中。對於後續的API請求,客戶端會在請求標頭中包含JWT,作為帶有“Bearer”方案的“Authorization”標頭。在您的PHP API中,您可以通過使用創建令牌時使用的相同密鑰來驗證其簽名來驗證傳入的JWT。此外,您還會檢查令牌是否未過期且包含有效的聲明。成功驗證後,您將從令牌聲明中提取用戶信息,並實現授權邏輯以確保用戶具有訪問請求資源所需的權限。 保持密鑰安全至關重要,因為它對於簽署和驗證令牌都至關重要。此密鑰的任何洩露都可能導致嚴重的安全性漏洞。
以上是JWT(JSON Web令牌)的PHP授權的詳細內容。更多資訊請關注PHP中文網其他相關文章!