Spring框架整合Java Web Token
Java Web Token
JSON Web Token(JWT)是一個非常輕巧的規範。這個規範允許我們使用JWT在使用者和伺服器之間傳遞安全可靠的資訊。
JWT組成
一個JWT實際上就是一個字串,它由三部分組成,頭部、載荷與簽名。
荷載
{
"iss": "John Wu JWT",
"iat": 1441593502,
"exp": 1441594722,
"aud": "www.example.com",
"sub": "[email protected]",
"from_user": "B",
"target_user": "A"
}
這裡面的前五個欄位都是由JWT的標準所定義的。
- iss: 該JWT的簽發者
- sub: 該JWT所面向的使用者
- aud: 接收該JWT的一方
- exp(expires): 什麼時候過期,這裡是一個Unix時間戳
- iat(issued at): 在什麼時候簽發的
這些定義都可以在標準中找到。
將上面的JSON物件進行[base64編碼]可以得到下面的字串。這個字串我們將它稱作JWT的Payload(載荷)。
eyJpc3MiOiJKb2huIFd1IEpXVCIsImlhdCI6MTQ0MTU5MzUwMiwiZXhwIjoxNDQxNTk0NzIyLCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJqcm9ja2V0QGV4YW1wbGUuY29tIiwiZnJvbV91c2VyIjoiQiIsInRhcmdldF91c2VyIjoiQSJ9
頭部
JWT還需要一個頭部,頭部用於描述關於該JWT的最基本的資訊,例如其型別以及簽名所用的演算法等。這也可以被表示成一個JSON物件。
{
“typ”: “JWT”,
“alg”: “HS256”
}
在這裡,我們說明了這是一個JWT,並且我們所用的簽名演算法(後面會提到)是HS256演算法。(演算法根據實際情況而變)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
簽名
簽名就是將荷載和頭部編碼用.號連線在一起就形成了
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0
我們將上面拼接完的字串用HS256演算法進行加密。在加密的時候,我們還需要提供一個金鑰(secret)。如果我們用mystar作為金鑰的話,那麼就可以得到我們加密後的內容
rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
最後將這一部分簽名也拼接在被簽名的字串後面,我們就得到了完整的JWT
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcm9tX3VzZXIiOiJCIiwidGFyZ2V0X3VzZXIiOiJBIn0.rSWamyAYwuHCo7IFAgd1oRpSP7nzL7BF5t7ItqpKViM
簽名的目的
最後一步簽名的過程,實際上是對頭部以及載荷內容進行簽名。一般而言,加密演算法對於不同的輸入產生的輸出總是不一樣的。對於兩個不同的輸入,產生同樣的輸出的概率極其地小(有可能比我成世界首富的概率還小)。所以,我們就把“不一樣的輸入產生不一樣的輸出”當做必然事件來看待吧。
所以,如果有人對頭部以及載荷的內容解碼之後進行修改,再進行編碼的話,那麼新的頭部和載荷的簽名和之前的簽名就將是不一樣的。而且,如果不知道伺服器加密的時候用的金鑰的話,得出來的簽名也一定會是不一樣的。
伺服器應用在接受到JWT後,會首先對頭部和載荷的內容用同一演算法再次簽名。那麼伺服器應用是怎麼知道我們用的是哪一種演算法呢?別忘了,我們在JWT的頭部中已經用alg欄位指明瞭我們的加密演算法了。
如果伺服器應用對頭部和載荷再次以同樣方法簽名之後發現,自己計算出來的簽名和接受到的簽名不一樣,那麼就說明這個Token的內容被別人動過的,我們應該拒絕這個Token,返回一個HTTP 401 Unauthorized響應。
我們可以看到,JWT適合用於向Web應用傳遞一些非敏感資訊。例如在上面提到的完成加好友的操作,還有諸如下訂單的操作等等。
其實JWT還經常用於設計使用者認證和授權系統,甚至實現Web應用的單點登入。
Spring使用JWT
Maven配置方式
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.2.0</version>
</dependency>
JWT演算法(瞭解)
JWS | 演算法 | 介紹 |
---|---|---|
HS256 | HMAC256 | HMAC with SHA-256 |
HS384 | HMAC384 | HMAC with SHA-384 |
HS512 | HMAC512 | HMAC with SHA-512 |
RS256 | RSA256 | RSASSA-PKCS1-v1_5 with SHA-256 |
RS384 | RSA384 | RSASSA-PKCS1-v1_5 with SHA-384 |
RS512 | RSA512 | RSASSA-PKCS1-v1_5 with SHA-512 |
ES256 | ECDSA256 | ECDSA with curve P-256 and SHA-256 |
ES384 | ECDSA384 | ECDSA with curve P-384 and SHA-384 |
ES512 | ECDSA512 | ECDSA with curve P-521 and SHA-512 |
使用方法
選擇演算法
演算法定義了一個令牌是如何被簽名和驗證的。它可以用HMAC演算法的原始值來例項化,也可以在RSA和ECDSA演算法的情況下對金鑰對或金鑰提供程式進行例項化。建立後,該例項可用於令牌簽名和驗證操作。
在使用RSA或ECDSA演算法時,只需要簽署JWTs,就可以通過傳遞null值來避免指定公鑰。當您需要驗證JWTs時,也可以使用私鑰進行操作
使用靜態的字元密文或者key來獲取演算法器:
//HMAC
Algorithm algorithmHS = Algorithm.HMAC256("secret");
//RSA
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
Algorithm algorithmRS = Algorithm.RSA256(publicKey, privateKey);
使用一個key提供者來獲取演算法:
通過使用KeyProvider,您可以在執行時更改金鑰,用於驗證令牌簽名或為RSA或ECDSA演算法簽署一個新的令牌。這是通過實現RSAKeyProvider或ECDSAKeyProvider方法實現的:
- getPublicKeyById(String kid): 它在令牌簽名驗證中呼叫,它應該返回用於驗證令牌的金鑰。如果使用了關鍵的輪換,例如JWK,它可以使用id來獲取正確的輪換鍵(或者只是一直返回相同的鍵)。
- getPrivateKey(): 在令牌簽名期間呼叫它,它應該返回用於簽署JWT的金鑰。
- getPrivateKeyId():在令牌簽名期間呼叫它,它應該返回標識由getPrivateKey()返回的鍵的id的id。這個值比JWTCreator.Builder和keyid(String)方法中的值更受歡迎。如果您不需要設定孩子的值,就避免使用KeyProvider例項化演算法。
建立JWT
try {
Algorithm algorithm = Algorithm.HMAC256("secret");
String token = JWT.create()
.withIssuer("auth0")
.sign(algorithm);
} catch (UnsupportedEncodingException exception){
//UTF-8 encoding not supported
} catch (JWTCreationException exception){
//Invalid Signing configuration / Couldn't convert Claims.
}
如果Claim不能轉換為JSON,或者在簽名過程中使用的金鑰無效,那麼將會丟擲JWTCreationException異常。
驗證令牌
首先需要通過呼叫jwt.require()和傳遞演算法例項來建立一個JWTVerifier例項。如果您要求令牌具有特定的Claim值,請使用構建器來定義它們。方法build()返回的例項是可重用的,因此您可以定義一次,並使用它來驗證不同的標記。最後呼叫verifier.verify()來驗證token
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
try {
Algorithm algorithm = Algorithm.HMAC256("secret");
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
} catch (UnsupportedEncodingException exception){
//UTF-8 encoding not supported
} catch (JWTVerificationException exception){
//Invalid signature/claims
}
時間驗證
當驗證一個令牌時,時間驗證會自動發生,導致在值無效時丟擲一個JWTVerificationException。如果前面的任何一個欄位都丟失了,那麼在這個驗證中就不會考慮這些欄位。
要指定令牌仍然被認為有效的餘地視窗,在JWTVerifier builder中使用accept迴旋()方法,並傳遞一個正值的秒值。這適用於上面列出的每一項。
JWTVerifier verifier = JWT.require(algorithm)
.acceptLeeway(1) // 1 sec for nbf, iat and exp
.build();
您還可以為給定的日期宣告指定一個自定義值,併為該宣告覆蓋預設值。
JWTVerifier verifier = JWT.require(algorithm)
.acceptLeeway(1) //1 sec for nbf and iat
.acceptExpiresAt(5) //5 secs for exp
.build();
資訊解析
Algorithm (“alg”)
返回jwt的演算法值或,如果沒有定義則返回null
String algorithm = jwt.getAlgorithm();
如果您需要在您的lib/app中測試此行為,將驗證例項轉換為basever視覺化,以獲得verific.build()方法的可見性,該方法可以接受定製的時鐘。例如:
BaseVerification verification = (BaseVerification) JWT.require(algorithm)
.acceptLeeway(1)
.acceptExpiresAt(5);
Clock clock = new CustomClock(); //Must implement Clock interface
JWTVerifier verifier = verification.build(clock);
Type (“typ”)
返回jwt的型別值,如果沒有定義則返回null(多數情況型別值為jwt)
String type = jwt.getType();
Content Type (“cty”)
返回內容的型別,如果沒有定義則返回null
String contentType = jwt.getContentType();
Key Id (“kid”)
返回key的id值,如果沒有定義則返回null
String keyId = jwt.getKeyId();
自定義欄位
在令牌的頭部中定義的附加宣告可以通過呼叫getHeaderClaim() 獲取,即使無法找到,也會返回。您可以通過呼叫claim.isNull()來檢查宣告的值是否為null。
Claim claim = jwt.getHeaderClaim("owner");
當使用jwt.create()建立一個令牌時,您可以通過呼叫withHeader()來指定頭宣告,並同時傳遞宣告的對映。
Map<String, Object> headerClaims = new HashMap();
headerClaims.put("owner", "auth0");
String token = JWT.create()
.withHeader(headerClaims)
.sign(algorithm);
提示:在簽名過程之後,alg和typ值將始終包含在Header中。
JWT的負載(Payload)宣告
Issuer ("iss")
返回簽發者的名稱值,如果沒有在負載中定義則返回null
String issuer = jwt.getIssuer();
Subject ("sub")
返回jwt所面向的使用者的值,如果沒有在負載中定義則返回null
String subject = jwt.getSubject();
Audience ("aud")
返回該jwt由誰接收,如果沒有在負載中定義則返回null
List<String> audience = jwt.getAudience();
Expiration Time ("exp")
返回該jwt的過期時間,如果在負載中沒有定義則返回null
Date expiresAt = jwt.getExpiresAt();
Not Before ("nbf")
Returns the Not Before value or null if it’s not defined in the Payload.
Date notBefore = jwt.getNotBefore();
Issued At ("iat")
返回在什麼時候簽發的,如果在負載中沒有定義則返回null
Date issuedAt = jwt.getIssuedAt();
JWT ID ("jti")
返回該jwt的唯一標誌,如果在負載中沒有定義則返回null
String id = jwt.getId();
自定義宣告
在令牌有效負載中定義的附加宣告可以通過呼叫getClaims()或 getClaim()和傳遞宣告名來獲得。即使無法找到宣告,也將會有返回值。您可以通過呼叫claim.isNull()來檢查宣告的值是否為null。
Map<String, Claim> claims = jwt.getClaims(); //Key is the Claim name
Claim claim = claims.get("isAdmin");
或者:
Claim claim = jwt.getClaim("isAdmin");
當使用jwt.create()建立一個令牌時,您可以通過呼叫withClaim()來指定自定義宣告,並同時傳遞名稱和值。
String token = JWT.create()
.withClaim("name", 123)
.withArrayClaim("array", new Integer[]{1, 2, 3})
.sign(algorithm);
您還可以通過呼叫withClaim()來驗證jwt.require()的自定義宣告,並傳遞該名稱和所需的值。
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("name", 123)
.withArrayClaim("array", 1, 2, 3)
.build();
DecodedJWT jwt = verifier.verify("my.jwt.token");
例項
package course.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.google.common.collect.Maps;
import course.pojo.User;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;
public class JwtUtils {
//建立token
public static String creatToken(User user) throws IllegalArgumentException, UnsupportedEncodingException{
Algorithm algorithm = Algorithm.HMAC256("secret");
String username = user.getUsername();
Map<String, Object> map = Maps.newHashMap();
map.put("alg", "HS256");
map.put("typ", "JWT");
String token = JWT.create().withHeader(map)
.withClaim("username", username)
.withExpiresAt(new Date(System.currentTimeMillis()+360000))
.sign(algorithm);
return token;
}
//驗證jwt
public static DecodedJWT verifyJwt(String token){
DecodedJWT decodedJWT = null;
try{
Algorithm algorithm = Algorithm.HMAC256("secret");
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
decodedJWT = jwtVerifier.verify(token);
}catch(IllegalArgumentException e){
e.printStackTrace();
}catch (UnsupportedEncodingException e){
e.printStackTrace();
}catch(JWTVerificationException e) {
e.printStackTrace();
}
return decodedJWT;
}
public static void main(String[] args) throws UnsupportedEncodingException{
// String username = "root";
// Integer id =1;
// System.out.println(creatToken(username,id));
// String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDgxMzgxNDAsInVzZXJJZCI6MSwidXNlcm5hbWUiOiJyb290In0.OeRdHJZKmxFBqIN-A-uSNQK8JyKdzX-wcFR883oMqFA";
// System.out.println(verifyJwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDgxMzgxNDAsInVzZXJJZCI6MSwidXNlcm5hbWUiOiJyb290In0.OeRdHJZKmxFBqIN-A-uSNQK8JyKdzX-wcFR883oMqFA"));
// System.out.println(verifyJwt(token).getClaims().get("username").asString());
}
}
相關推薦
Spring框架整合Java Web Token
Java Web Token JSON Web Token(JWT)是一個非常輕巧的規範。這個規範允許我們使用JWT在使用者和伺服器之間傳遞安全可靠的資訊。 JWT組成 一個JWT實際上就是一個字串,它由三部分組成,頭部、載荷與簽名。 荷載 {
Spring框架整合WEB解決配置檔案載入多次的問題
1. 建立JavaWEB專案,引入Spring的開發包。編寫具體的類和方法。 * 環境搭建好後,啟動伺服器來測試專案,傳送每訪問一次都會載入一次配置檔案,這樣效率會非常非常慢!! 2. 解決上面的問題 * 將工廠建立好了以後放入到ServletContext域中.使用工廠的時候,從Servl
Spring框架整合WEB解決配置文件加載多次的問題
load cti style customer ner ssp text param work 1. 創建JavaWEB項目,引入Spring的開發包。編寫具體的類和方法。 * 環境搭建好後,啟動服務器來測試項目,發送每訪問一次都會加載一次配置文件,這樣效率會非常非
3 Spring框架整合WEB 1(與struts2整合)
使用spring與struts2整合 web.xml的配置 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-in
idea中建立一個spring springMVC miniui框架的Java web專案的經過
最近學習miniui,發現網上可查的資料太少,基本只有官方api文件,在此,記錄下最近學習的部分內容。一、安裝部署專案。 本人是在idea中進行部署的,但在此之前,具體是將網上官方的示例下載下來,匯入MyEclipse文件中首次進行學習,但是第一次將專案按照“現在已經存
SSM Spring SpringMVC Mybatis框架整合Java配置完整版
以前用著SSH都是老師給配好的,自己直接改就可以。但是公司主流還是SSM,就自己研究了一下Java版本的配置。網上大多是基於xnl的配置,但是越往後越新的專案都開始基於JavaConfig配置了,這也是寫此文章的原因。不論是eclipse還是myeclipse 都沒有整合mybatis的相關元件,Spri
複習電商筆記-35-常見問題、RedisCluster和Spring框架整合
常見問題 another app is currently holding the yum…. 直接 ps –ef|grep yum 殺掉就行了 ERR Slot 5798 is already busy Ca
基於spring框架的java開發中的異常處理
在springmvc框架的中異常處理的方式有兩種: 1,在控制器中使用@ExceptionHandler(xxxException.class)註解修飾一個方法,該註解能夠處理通一個控制器類中的丟擲的xxxExcepiton異常。 使用控制器通知註解@ControllerAdvice
阿里老司機帶你使用Spring框架快速搭建Web工程專案
演講嘉賓簡介 呂德慶(花名:嵛山), 阿里巴巴高階開發工程師,武漢大學地信碩士,有豐富的系統開發經驗,目前就職於阿里巴巴程式碼中心團隊,負責後端開發。 本文首先將介紹Spring框架的相關概念,其次將藉助Spring Web示例工程帶大家學習如何快速開發Spring Web應用。 一、Sprin
Spring框架整合Struts2框架的傳統方法
1. 匯入CRM專案的UI頁面,找到新增客戶的頁面,修改form表單,訪問Action * 將menu.jsp中133行的新增客戶的跳轉地址改為:href="${pageContext.request.contextPath}/jsp/customer/add.jsp" * 將jsp/custom
APP使用者登入解決方案——JWT(java web token)生成Token
什麼是JSON Web Token?JSON Web Token(JWT)是一個開放式標準(RFC 7519),它定義了一種緊湊且自包含的方式,用於在各方之間以JSON物件安全傳輸資訊。這些資訊可以通過數字簽名進行驗證和信任。可以使用祕密(使用HMAC演算法)或使用RSA的公
ssm框架整合配置---web.xml,傻傻分不清楚
web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:x
最簡單的SSM框架整合_java web普通版
最簡單的SSM框架整合_java web專案普通版 1. 前言 筆者在做javaweb專案時,用SSM+maven+easyui/bootstarp,接著上一個博文,我們梳理了SSM框架下的每層的作用和聯絡。詳情點選。而在這篇博文中,記錄的是SSM框架整
【本人禿頂程式設計師】Spring Boot整合Java DSL
←←←←←←←←←←←← 快,點關注! Spring Integration Java DSL已經融合到Spring Integration Core 5.0,這是一個聰明而明顯的舉動,因為: 基於Java Config啟動新Spring專案的每個人都使用它 SI Ja
初識JWT(java web token)
JWT(Json Web Token)是JSON風格輕量級的授權和身份認證規範,可實現無狀態、分散式的Web應用授權。個人理解 我認為它是分散式session的替代物,在沒有jwt之前,我們可以用re
基於Spring框架開發的Web程式,如何動態修改日誌級別
背景: 線上環境日誌級別一般較高,出現故障定位過程中,期望輸出儘可能完備的日誌資訊便於除錯。日誌級別動態修改就是一個不錯的思路,幸運的是基於Spring框架開發的Web程式,藉助spring-web包中org.springframework.web.util.Log4jCo
spring框架整合ibatis的專案例項程式碼
這兩天一直在研究ibatis與spring的整合 一個小小的demo搞的我頭暈目眩的,但程式一旦跑起來了,突然有一種豁然開朗,重見天日,感覺生活很美好的感覺!,也許,這就是那一行行的程式碼帶給我們的不同享受吧。呵呵,廢話就不多說了。 在此先引用幾句別人的資料。。。
java-web系列(一)---搭建一個基於SSM框架的java-web專案
前言 extensible專案當前功能模組如下: 如對該專案有疑問,可在我的部落格/github下面留言,也可以以郵件的方式告知。 我的聯絡方式:[email protected] extensible 這是一個基礎的java web專案。後期我會
activeMQ開發筆記,activeMQy與Spring框架整合
MQ開發其實很簡單: 本文以最新版本的ActiveMQ為例子,介紹了安裝和開發第一個MQ程式。 準備環境,JDK8,activeMQ 5.14.3,WIN7測試環境,Spring 4.3.4 首先是安裝 : 安裝MQ非常簡單,下載連結:https://activemq
JWT- Java Web Token 原理
JWT是一個輕量級的規範,之前學習過,但一直沒有什麼應用。最近在做一個OAuth 2相關的專案,有用到JWT。於是,再複習一遍。JWT是什麼?JSON Web Token (JWT) 是一個簡潔的,自包含的,可安全的在兩個模組之間傳輸。這是JWT.io給出的定義。簡潔: JW