1. 程式人生 > >Spring框架整合Java Web Token

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