Java应用程序的令牌认证
建筑物身份管理,包括身份驗證和授權? 嘗試Stormpath! 我們的REST API和強大的Java SDK支持可以消除您的安全風險,并且可以在幾分鐘內實現。 注冊 ,再也不會建立auth了!
2016年5月12日更新:構建Java應用程序? JJWT是一個Java庫,由我們自己的Les Hazlewood開發,提供端到端JWT的創建和驗證。 JJWT永久免費且開源(Apache許可證,版本2.0),易于使用和理解。 它的設計采用了以構建者為中心的流暢界面,從而掩蓋了其大部分復雜性。 我們希望您能嘗試一下 ,并告訴我們您的想法! (并且,如果您是Node開發人員,請查看NJWT !)
在我的上一篇文章中 ,我們涵蓋了很多基礎,包括我們傳統上如何保護網站安全,使用cookie和會話的一些陷阱以及如何通過傳統方式解決這些陷阱。
在本文中,我們將超越傳統,深入探討如何使用JWT(JSON Web令牌)進行令牌身份驗證,不僅解決了這些問題,而且還為我們提供了可檢查的元數據和強大的加密簽名的好處。
救援的令牌認證!
首先讓我們檢查一下在這種情況下authentication和token含義。
身份驗證證明用戶就是他們所說的真實身份。
令牌是一個獨立的信息塊。 它可能具有內在價值,也可能沒有。 我們將研究一種特定類型的令牌, 它確實具有內在價值,并通過會話ID解決了許多問題。
JSON Web令牌(JWT)
JWT是URL安全,緊湊,自包含的字符串,其中包含有意義的信息,這些信息通常經過數字簽名或加密。 它們正Swift成為網絡上令牌實施的實際標準。
URL安全是一種說法,可以說整個字符串都已編碼,因此沒有特殊字符,并且令牌可以放入URL中。
該字符串是不透明的,可以與使用會話ID幾乎相同的方式獨立使用。 不透明是指查看字符串本身沒有提供任何其他信息。
但是,還可以對字符串進行解碼以提取元數據,并且可以對簽名進行加密驗證,以使您的應用程序知道令牌未被篡改。
JWT和OAuth2訪問令牌
許多OAuth2實現都將JWT用于其訪問令牌。 應該指出的是,OAuth2和JWT規范是彼此完全獨立的,彼此之間沒有任何依賴關系。 將JWT用作OAuth2的令牌機制會帶來很多好處,我們將在下面看到。
JWT可以存儲在cookie中,但是我們之前討論的cookie的所有規則仍然適用。 您可以將會話ID完全替換為JWT。 然后,您可以獲得直接從該會話ID訪問元信息的額外好處。
在野外,它們看起來就像是另一個丑陋的弦:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vdHJ1c3R5YXBwLmNvbS8iLCJleHAiOjEzMDA4MTkzODAsInN1YiI6InVzZXJzLzg5ODM0NjIiLCJzY29wZSI6InNlbGYgYXBpL2J1eSJ9.43DXvhrwMGeLLlP4P4izjgsBB2yrpo82oiUPhADakLs如果仔細看,您會發現字符串中有兩個句點。 這些意義重大,因為它們界定了JWT的不同部分。
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 . eyJpc3MiOiJodHRwOi8vdHJ1c3R5YXBwLmNvbS8iLCJleHAiOjEzMDA4MTkzODAsInN1YiI6InVzZXJzLzg5ODM0NjIiLCJzY29wZSI6InNlbGYgYXBpL2J1eSJ9 . 43DXvhrwMGeLLlP4P4izjgsBB2yrpo82oiUPhADakLsJWT結構
JWT具有三部分結構,每個部分都是base64編碼的:
以下是解碼的部分:
標頭
{"typ": "JWT","alg": "HS256" }索償
{"iss":"http://trustyapp.com/","exp": 1300819380,"sub": "users/8983462","scope": "self api/buy" }密碼簽名
t?′—?à%O?v+n?…SZuˉμ€U…8H×智威湯遜索賠
讓我們檢查索賠部分。 可以在此處找到屬于JWT規范的每種聲明。
iss是發行令牌的人。
exp是令牌過期的時間。
sub是令牌的主題。 這通常是某種用戶標識符。
權利要求的以上部分全部包含在JWT規范中。 scope未包含在規范中,但通常用于提供授權信息。 也就是說,用戶可以訪問應用程序的哪些部分。
JWT的優點之一是,可以將任意數據編碼到上述scope的權利要求中。 另一個優點是,客戶端現在可以對該信息做出反應,而無需與服務器進行任何進一步的交互。 例如,可以基于在scope權利要求中找到的數據來隱藏頁面的一部分。
注意 :服務器始終驗證客戶機執行的操作仍然很關鍵,也是最佳實踐。 例如,如果在客戶端上執行了某些管理操作,則您仍想在應用程序服務器上驗證當前用戶是否有權執行該操作。 您永遠不會僅依賴客戶端授權信息。
您可能已經獲得了另一個優勢:加密簽名。 簽名可以被驗證,證明JWT未被篡改。 請注意,密碼簽名的存在并不保證機密性。 僅當對JWT進行加密和簽名時,才能確保機密性。
現在,最重要的是: 無國籍 。 盡管服務器將需要生成JWT,但是它不需要將其存儲在任何地方,因為所有用戶元數據都已直接編碼到JWT中。 服務器和客戶端可以來回傳遞JWT,而從不存儲它。 這樣可以很好地擴展。
管理承載令牌安全性
隱式信任是一種折衷。 這些類型的令牌通常稱為Bearer令牌,因為獲得對應用程序受保護部分的訪問所需的全部就是有效的未過期令牌的表示。
您必須解決以下問題:令牌應使用多長時間? 您將如何撤銷它? (還有其他整篇文章可以針對刷新令牌進行 。)
如果未加密,則必須注意存儲在JWT中的內容。 不要存儲任何敏感信息。 以sub權利要求的形式存儲用戶標識符是一種普遍接受的做法。 當JWT簽名時,它稱為JWS。 加密后,稱為JWE。
Java,JWT和您!
我們為Github上的JJWT項目感到自豪。 它是由Stormpath的首席技術官Les Hazlewood最初撰寫的,它是針對Java的完全開源的JWT解決方案。 它是最容易使用和理解的庫,用于在JVM上創建和驗證JSON Web令牌。
您如何創建JWT? 十分簡單!
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm;byte[] key = getSignatureKey();String jwt = Jwts.builder().setIssuer("http://trustyapp.com/").setSubject("users/1300819380").setExpiration(expirationDate).put("scope", "self api/buy") .signWith(SignatureAlgorithm.HS256,key).compact();首先要注意的是用于創建JWT的流暢的 Builder API。 方法調用被鏈接在一起,最終以compact調用返回最終的JWT字符串。
還要注意,當我們設置規范中的一項權利要求時,我們使用了二傳手。 例如: .setSubject("users/1300819380") 。 設置自定義聲明后,我們會使用調用來放置并指定鍵和值。 例如: .put("scope", "self api/buy")
驗證JWT一樣容易。
String subject = "HACKER"; try {Jws jwtClaims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt);subject = claims.getBody().getSubject();//OK, we can trust this JWT} catch (SignatureException e) {//don't trust the JWT! }如果對JWT進行了任何篡改,則解析聲明將引發SignatureException并且subject變量的值將保持HACKER 。 如果它是有效的JWT,則將從中提取subject : claims.getBody().getSubject()
什么是OAuth?
在下一節中,我們將看一個使用Stormpath的OAuth2實現的示例,該實現利用了JWT。
OAuth2規范周圍有很多困惑。 那部分是因為它確實是über規范–它具有很多復雜性。 這也是因為OAuth1.a和OAuth2是非常不同的野獸。 我們將看一看OAuth2規范的一個非常具體,易于使用的子集。 我們有一篇很棒的文章,它詳細介紹了什么是OAuth 。 在這里,我們將提供一些簡短的背景知識,然后直接進入示例。
OAuth2基本上是一種支持授權工作流程的協議。 這意味著它為您提供了一種確保特定用戶有權執行某項操作的方法。
而已。
OAuth2 并不旨在執行諸如驗證用戶身份的工作, 而是由身份驗證服務負責。 身份驗證是在驗證用戶身份( 例如要求輸入用戶名/密碼 )時進行的驗證,而授權是在檢查現有用戶已擁有的權限時進行的驗證。
請記住,OAuth2是授權協議。
使用OAuth授權類型進行授權
讓我們看一下典型的OAuth2交互。
POST /oauth/token HTTP/1.1 Origin: https://foo.com Content-Type: application/x-www-form-urlencodedgrant_type=password&username=username&password=passwordgrant_type是grant_type 。 這種交互類型也需要application/x-www-form-urlencoded內容類型。 假設您通過網絡傳遞用戶名和密碼,則始終希望連接是安全的。 不過,好消息是,響應將具有OAuth2承載令牌。 然后,此令牌將用于以后瀏覽器與服務器之間的每次交互。 這里有一個非常簡短的介紹,其中用戶名和密碼是通過網絡傳遞的。 假設服務器上的身份驗證服務驗證了用戶名和密碼,則響應如下:
HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache{"access_token":"2YotnFZFEjr1zCsicMWpAA...","token_type":"example","expires_in":3600,"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA...","example_parameter":"example_value" }注意Cache-Control和Pragma標頭。 我們不希望此響應被緩存在任何地方。 access_token是瀏覽器在后續請求中將使用的內容。 同樣,OAuth2和JWT之間沒有直接關系。 但是, access_token可以是JWT。 這就是編碼的元數據的額外好處所在。這是在以后的請求中利用訪問令牌的方式:
GET /admin HTTP/1.1 Authorization: Bearer 2YotnFZFEjr1zCsicMW...Authorization標頭是標準標頭。 使用OAuth2不需要自定義標頭。 在這種情況下,類型是Bearer而不是Basic類型。 訪問令牌直接包含在Bearer關鍵字之后。 這樣就完成了密碼授予類型的O??Auth2交互。 瀏覽器的每個后續請求都可以使用Authorizaion: Bearer標頭和訪問令牌。
還有另一種稱為client_credentials授予類型,它使用client_id和client_secret而不是username和password 。 此授予類型通常用于API交互。 盡管客戶ID和秘密機密的功能類似于用戶名和密碼,但它們通常具有更高的質量安全性,并且不一定是人類可讀的。
帶我們回家:OAuth2 Java示例
我們到了! 現在該深入研究一些演示JWT的特定代碼了。
Spring Boot Web MVC
Stormpath Java SDK中有許多示例。 在這里,我們將看一個Spring Boot Web MVC示例。 這是示例中的HelloController :
@RestController public class HelloController {@RequestMapping("/")String home(HttpServletRequest request) {String name = "World";Account account = AccountResolver.INSTANCE.getAccount(request);if (account != null) {name = account.getGivenName();}return "Hello " + name + "!";}}為了演示的目的,關鍵是:
Account account = AccountResolver.INSTANCE.getAccount(request);
在幕后,僅當存在經過身份驗證的會話時, account才會解析為Account對象(而不是null )。
生成并運行示例代碼
要構建并運行此示例,請執行以下操作:
? dogeared jobs:0 ~/Projects/StormPath/stormpath-sdk-java (master|8100m) ? cd examples/spring-boot-webmvc/ ? dogeared jobs:0 ~/Projects/StormPath/stormpath-sdk-java/examples/spring-boot-webmvc (master|8100m) ? mvn clean package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building Stormpath Java SDK :: Examples :: Spring Boot Webapp 1.0.RC4.6-SNAPSHOT [INFO] ------------------------------------------------------------------------... skipped output ...[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.865 s [INFO] Finished at: 2015-08-04T11:46:05-04:00 [INFO] Final Memory: 31M/224M [INFO] ------------------------------------------------------------------------ ? dogeared jobs:0 ~/Projects/StormPath/stormpath-sdk-java/examples/spring-boot-webmvc (master|8100m啟動Spring Boot示例
然后,您可以像這樣啟動Spring Boot示例:
? dogeared jobs:0 ~/Projects/StormPath/stormpath-sdk-java/examples/spring-boot-webmvc (master|8104m) ? java -jar target/stormpath-sdk-examples-spring-boot-web-1.0.RC4.6-SNAPSHOT.jar. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v1.2.1.RELEASE)2015-08-04 11:51:00.127 INFO 17973 --- [ main] tutorial.Application : Starting Application v1.0.RC4.6-SNAPSHOT on MacBook-Pro.local with PID 17973 ... skipped output ...2015-08-04 11:51:04.558 INFO 17973 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http) 2015-08-04 11:51:04.559 INFO 17973 --- [ main] tutorial.Application : Started Application in 4.599 seconds (JVM running for 5.103)注意 :這假設您已經設置了一個Stormpath帳戶,并且您的api密鑰位于~/.stormpath/apiKey.properties 。 在此處查找有關使用Spring Boot快速設置Stormpath的更多信息。
使用JSON Web令牌進行身份驗證(或不進行身份驗證)
現在,我們可以練習該示例,并展示一些實際應用中的JWT! 首先,在沒有任何身份驗證的情況下命中端點。 我喜歡使用httpie ,但是任何命令行http客戶端都可以。
? http -v localhost:8080 GET / HTTP/1.1 Accept: */* Accept-Encoding: gzip, deflate Connection: keep-alive Host: localhost:8080 User-Agent: HTTPie/0.9.2HTTP/1.1 200 OK Accept-Charset: big5, big5-hkscs, cesu-8, euc-jp, euc-kr, gb18030, ... Content-Length: 12 Content-Type: text/plain;charset=UTF-8 Date: Tue, 04 Aug 2015 15:56:41 GMT Server: Apache-Coyote/1.1Hello World!-v參數產生詳細的輸出,并顯示請求和響應的所有標頭。 在這種情況下,輸出消息就是: Hello World! 。 這是因為沒有建立的會話。
使用Stormpath OAuth端點進行身份驗證
現在,讓我們點擊oauth端點,以便我們的服務器可以使用Stormpath進行身份驗證。 您可能會問,“什么是oauth端點?” 上面的控制器未指示任何此類端點。 示例中是否還有其他具有其他端點的控制器? 不是,沒有! Stormpath提供了開箱即用的oauth(和許多其他)端點。 一探究竟:
? http -v --form POST http://localhost:8080/oauth/token \ > 'Origin:http://localhost:8080' \ > grant_type=password username=micah+demo.jsmith@stormpath.com password= POST /oauth/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded; charset=utf-8 Host: localhost:8080 Origin: http://localhost:8080 User-Agent: HTTPie/0.9.2grant_type=password&username=micah%2Bdemo.jsmith%40stormpath.com&password=HTTP/1.1 200 OK Cache-Control: no-store Content-Length: 325 Content-Type: application/json;charset=UTF-8 Date: Tue, 04 Aug 2015 16:02:08 GMT Pragma: no-cache Server: Apache-Coyote/1.1 Set-Cookie: account=eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNmQxMy1mNThiLTRhNDEtYmVkZS0wYjM0M2ZjZDFhYzAiLCJpYXQiOjE0Mzg3MDQxMjgsInN1YiI6Imh0dHBzOi8vYXBpLnN0b3JtcGF0aC5jb20vdjEvYWNjb3VudHMvNW9NNFdJM1A0eEl3cDRXaURiUmo4MCIsImV4cCI6MTQzODk2MzMyOH0.wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4; Expires=Wed, 05-Aug-2015 16:02:08 GMT; Path=/; HttpOnly{"access_token": "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNmQxMy1mNThiLTRhNDEtYmVkZS0wYjM0M2ZjZDFhYzAiLCJpYXQiOjE0Mzg3MDQxMjgsInN1YiI6Imh0dHBzOi8vYXBpLnN0b3JtcGF0aC5jb20vdjEvYWNjb3VudHMvNW9NNFdJM1A0eEl3cDRXaURiUmo4MCIsImV4cCI6MTQzODk2MzMyOH0.wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4","expires_in": 259200,"token_type": "Bearer" }這里有很多事情,所以讓我們分解一下。
在第一行中,我告訴httpie我想創建一個表單url編碼的POST,這就是--form和POST參數的作用。 我正在本地運行的服務器的/oauth/token端點上。 我指定一個Origin標頭。 出于我們先前提到的安全原因,與Stormpath進行交互是必需的。 根據OAuth2規范,我要傳遞grant_type=password以及username和password 。
響應具有Set-Cookie標頭以及包含OAuth2訪問令牌的JSON正文。 你猜怎么著? 該訪問令牌也是JWT。 以下是已解碼的聲明:
{"jti": "14426d13-f58b-4a41-bede-0b343fcd1ac0","iat": 1438704128,"sub": "https://api.stormpath.com/v1/accounts/5oM4WI3P4xIwp4WiDbRj80","exp": 1438963328 }注意sub鍵。 這就是我驗證為的帳戶的完整Stormpath URL。 現在,讓我們再次點擊基本的Hello World端點,僅這次,我們將使用OAuth2訪問令牌:
? http -v localhost:8080 \ > 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNmQxMy1mNThiLTRhNDEtYmVkZS0wYjM0M2ZjZDFhYzAiLCJpYXQiOjE0Mzg3MDQxMjgsInN1YiI6Imh0dHBzOi8vYXBpLnN0b3JtcGF0aC5jb20vdjEvYWNjb3VudHMvNW9NNFdJM1A0eEl3cDRXaURiUmo4MCIsImV4cCI6MTQzODk2MzMyOH0.wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4' GET / HTTP/1.1 Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNmQxMy1mNThiLTRhNDEtYmVkZS0wYjM0M2ZjZDFhYzAiLCJpYXQiOjE0Mzg3MDQxMjgsInN1YiI6Imh0dHBzOi8vYXBpLnN0b3JtcGF0aC5jb20vdjEvYWNjb3VudHMvNW9NNFdJM1A0eEl3cDRXaURiUmo4MCIsImV4cCI6MTQzODk2MzMyOH0.wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4 Connection: keep-alive Host: localhost:8080 User-Agent: HTTPie/0.9.2HTTP/1.1 200 OK Content-Length: 11 Content-Type: text/plain;charset=UTF-8 Date: Tue, 04 Aug 2015 16:44:28 GMT Server: Apache-Coyote/1.1Hello John!請注意,在輸出的最后一行中,消息是通過名稱尋址我們的。 現在,我們已經使用OAuth2與Stormpath建立了經過身份驗證的會話,控制器中的以下幾行將檢索名字:
Account account = AccountResolver.INSTANCE.getAccount(request); if (account != null) {name = account.getGivenName(); }摘要:Java應用程序的令牌認證
在本文中,我們研究了使用JWT進行令牌身份驗證如何不僅解決傳統方法的問題,而且還為我們提供了可檢查的元數據和強大的密碼簽名的好處。
我們概述了OAuth2協議,并詳細介紹了Stormpath的OAuth2實現如何使用JWT。
以下是指向基于令牌的身份驗證,JWT和Spring Boot的文章的其他鏈接:
- Angular.js的基于令牌的身份驗證
- JJWT –適用于Java和Android的JSON Web令牌
- Spring Boot Webapp示例快速入門
- JWT規范
建筑物身份管理,包括身份驗證和授權? 嘗試Stormpath! 我們的REST API和強大的Java SDK支持可以消除您的安全風險,并且可以在幾分鐘內實現。 注冊 ,再也不會建立auth了!
翻譯自: https://www.javacodegeeks.com/2016/07/token-authentication-java-applications.html
總結
以上是生活随笔為你收集整理的Java应用程序的令牌认证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: e课堂电脑学校(下载E课堂)
- 下一篇: lambda ::_Lambda项目:迈