欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中的JWT使用詳解

 更新時間:2023年08月01日 09:45:06   作者:一支有理想的月月鳥  
這篇文章主要介紹了Java中的JWT使用詳解,JWT是一個開放標(biāo)準(zhǔn)(rfc7519),它定義了一種緊湊的、自包含的方式,用于在各方之間以JSON對象安全地傳輸信息,需要的朋友可以參考下

JWT

JSON Web Token(JSON Web令牌)

是一個開放標(biāo)準(zhǔn)(rfc7519),它定義了一種緊湊的、自包含的方式,用于在各方之間以JSON對象安全地傳輸信息。此信息可以驗證和信任,因為它是數(shù)字簽名的。

jwt可以使用秘密〈使用HNAC算法)或使用RSA或ECDSA的公鑰/私鑰對進行簽名。

通過JSON形式作為Web應(yīng)用中的令牌,用于在各方之間安全地將信息作為JSON對象傳輸。在數(shù)據(jù)傳輸過程中還可以完成數(shù)據(jù)加密、簽名等相關(guān)處理。

JWT作用:

  • 授權(quán):一旦用戶登錄,每個后續(xù)請求將包括JWT,從而允許用戶訪問該令牌允許的路由,服務(wù)和資源。它的開銷很小并且可以在不同的域中使用。如:單點登錄。
  • 信息交換:在各方之間安全地傳輸信息。JWT可進行簽名(如使用公鑰/私鑰對),因此可確保發(fā)件人。由于簽名是使用標(biāo)頭和有效負載計算的,因此還可驗證內(nèi)容是否被篡改。

1.傳統(tǒng)Session

1.1.認證方式

http協(xié)議本身是一種無狀態(tài)的協(xié)議,如果用戶向服務(wù)器提供了用戶名和密碼來進行用戶認證,下次請求時,用戶還要再一次進行用戶認證才行。

因為根據(jù)http協(xié)議,服務(wù)器并不能知道是哪個用戶發(fā)出的請求,所以為了讓我們的應(yīng)用能識別是哪個用戶發(fā)出的請求,我們只能在服務(wù)器存儲─份用戶登錄的信息,這份登錄信息會在響應(yīng)時傳遞給瀏覽器,告訴其保存為cookie,以便下次請求時發(fā)送給我們的應(yīng)用,這樣應(yīng)用就能識別請求來自哪個用戶。

1.2.暴露的問題

  • 用戶經(jīng)過應(yīng)用認證后,應(yīng)用都要在服務(wù)端做一次記錄,以方便用戶下次請求的鑒別,通常而言session都是保存在內(nèi)存中,而隨著認證用戶的增多,服務(wù)端的開銷會明顯增大;
  • 用戶認證后,服務(wù)端做認證記錄,如果認證的記錄被保存在內(nèi)存中的話,用戶下次請求還必須要請求在這臺服務(wù)器上,這樣才能拿到授權(quán)的資源。在分布式的應(yīng)用上,限制了負載均衡器的能力。以此限制了應(yīng)用的擴展能力;
  • session是基于cookie來進行用戶識別,cookie如果被截獲,用戶很容易受到CSRF(跨站偽造請求攻擊)攻擊;
  • 在前后端分離系統(tǒng)中應(yīng)用解耦后增加了部署的復(fù)雜性。通常用戶一次請求就要轉(zhuǎn)發(fā)多次。如果用session每次攜帶sessionid到服務(wù)器,服務(wù)器還要查詢用戶信息。同時如果用戶很多。這些信息存儲在服務(wù)器內(nèi)存中,給服務(wù)器增加負擔(dān)。還有就是sessionid就是一個特征值,表達的信息不夠豐富。不容易擴展。而且如果你后端應(yīng)用是多節(jié)點部署。那么就需要實現(xiàn)session共享機制。不方便集群應(yīng)用。

2.JWT認證

2.1.認證流程

  • 前端通過Web表單將自己的用戶名和密碼發(fā)送到后端的接口。該過程一般是HTTP的POST請求。建議的方式是通過SSL加密的傳輸(https協(xié)議),從而避免敏感信息被嗅探。
  • 后端核對用戶名和密碼成功后,將用戶的id等其他信息作為JWT Payload(負載),將其與頭部分別進行Base64編碼拼接后簽名,形成一個JWT(Token)。
  • 后端將JWT字符串作為登錄成功的返回結(jié)果返回給前端。前端可以將返回的結(jié)果保存在localStorage(瀏覽器本地緩存)或sessionStorage(session緩存)上,退出登錄時前端刪除保存的JWT即可。
  • 前端在每次請求時將JWT放入HTTP的Header中的Authorization位。(解決XSS和XSRF問題)HEADER
  • 后端檢查是否存在,如存在驗證JWT的有效性。例如,檢查簽名是否正確﹔檢查Token是否過期;檢查Token的接收方是否是自己(可選)
  • 驗證通過后后端使用JWT中包含的用戶信息進行其他邏輯操作,返回相應(yīng)結(jié)果。

2.2.JWT優(yōu)點

  • 簡潔(Compact):可以通過URL,POST參數(shù)或者在HTTP header發(fā)送,數(shù)據(jù)量小,傳輸速度也很快;
  • 自包含(Self-contained):負載中包含了所有用戶所需要的信息,避免了多次查詢數(shù)據(jù)庫;
  • Token是以JSON加密的形式保存在客戶端,所以JWT是跨語言的,原則上任何web形式都支持。
  • 不需要在服務(wù)端保存會話信息,特別適用于分布式微服務(wù)。

3.JWT結(jié)構(gòu)

就是令牌token,是一個String字符串,由3部分組成,中間用點隔開

令牌組成:

  1. 標(biāo)頭(Header)
  2. 有效載荷(Payload)
  3. 簽名(Signature)

token格式:head.payload.singurater 如:xxxxx.yyyy.zzzz

  • Header:有令牌的類型和所使用的簽名算法,如HMAC、SHA256、RSA;使用Base64編碼組成;(Base64是一種編碼,不是一種加密過程,可以被翻譯成原來的樣子)
{
	"alg" : "HS256",
	"type" : "JWT"
}
  • Payload :有效負載,包含聲明;聲明是有關(guān)實體(通常是用戶)和其他數(shù)據(jù)的聲明,不放用戶敏感的信息,如密碼。同樣使用Base64編碼
{
	"sub" : "123",
	"name" : "John Do",
	"admin" : true
}
  • Signature :前面兩部分都使用Base64進行編碼,前端可以解開知道里面的信息。Signature需要使用編碼后的header和payload 加上我們提供的一個密鑰,使用header中指定的簽名算法(HS256)進行簽名。簽名的作用是保證JWT沒有被篡改過
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);

**簽名目的:**簽名的過程實際上是對頭部以及負載內(nèi)容進行簽名,防止內(nèi)容被竄改。如果有人對頭部以及負載的內(nèi)容解碼之后進行修改,再進行編碼,最后加上之前的簽名組合形成新的JWT的話,那么服務(wù)器端會判斷出新的頭部和負載形成的簽名和JWT附帶上的簽名是不一樣的。如果要對新的頭部和負載進行簽名,在不知道服務(wù)器加密時用的密鑰的話,得出來的簽名也是不一樣的。

信息安全問題:Base64是一種編碼,是可逆的,適合傳遞一些非敏感信息;JWT中不應(yīng)該在負載中加入敏感的數(shù)據(jù)。如傳輸用戶的ID被知道也是安全的,如密碼不能放在JWT中;JWT常用于設(shè)計用戶認證、授權(quán)系統(tǒng)、web的單點登錄。

4.JWT使用

4.1.引入依賴

<!--引入JWT-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.0</version>
</dependency>

4.2.生成token

HashMap<String,Object> map = new HashMap<>();
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,20);
        String token = JWT.create()
                .withHeader(map) //可以不設(shè)定,就是使用默認的
                .withClaim("userId",20)//payload  //自定義用戶名
                .withClaim("username","zhangsan")
                .withExpiresAt(instance.getTime()) //指定令牌過期時間
                .sign(Algorithm.HMAC256("fdahuifeuw78921"));//簽名

4.3.根據(jù)令牌和簽名解析數(shù)據(jù)

JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("fdahuif921")).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        decodedJWT.getClaim("userId").asInt();//獲取負載里面對應(yīng)的內(nèi)容
        decodedJWT.getClaim("username").asString();
        decodedJWT.getExpiresAt();//獲取過期時間

4.4.常見異常信息

SignatureVerificationException //簽名不一致異常
TokenExpiredException //令牌過期異常
AlgorithmMismatchException //算法不匹配異常
InvalidClaimException //失效的payload異常(傳給客戶端后,token被改動,驗證不一致)

5.封裝工具類

public class JWTUtils {
    private static String SIGNATURE = "token!@#$%^7890";
    /**
     * 生成token
     * @param map //傳入payload
     * @return 返回token
     */
    public static String getToken(Map<String,String> map){
        JWTCreator.Builder builder = JWT.create();
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,7);
        builder.withExpiresAt(instance.getTime());
        return builder.sign(Algorithm.HMAC256(SIGNATURE)).toString();
    }
    /**
     * 驗證token
     * @param token
     */
    public static void verify(String token){
        JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
    }
    /**
     * 獲取token中payload
     * @param token
     * @return
     */
    public static DecodedJWT getToken(String token){
        return JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);
    }
}

6.SpringBoot整合JWT

6.1.登錄時生成token

//controller層接收數(shù)據(jù),生成token,并響應(yīng)
Map<String,Object> map = new HashMap<>();
try{
    User userDB = userService.login(user);
    Map<String,String> payload = new HashMap<>();
    payload.put("id",userDB.getId());
    payload.put("name",userDB.getName());
    //生成JWT令牌
    String token = JWTUtils.getToken(payload);
    map.put("state",true);
    map.put("msg","認證成功");
    map.put("token",token);//響應(yīng)token
} catch (Exception e) {
    map.put("state","false");
    map.put("msg",e.getMessage());
}

6.2.聲明一個token攔截器類

package com.liup.interceptor;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.office.utils.JWTUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
 * JWT驗證攔截器
 */
public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String,Object> map = new HashMap<>();
        //令牌建議是放在請求頭中,獲取請求頭中令牌
        String token = request.getHeader("token");
        try{
            JWTUtils.verify(token);//驗證令牌
            return true;//放行請求
        } catch (SignatureVerificationException e) {
            e.printStackTrace();
            map.put("msg","無效簽名");
        } catch (TokenExpiredException e) {
            e.printStackTrace();
            map.put("msg","token過期");
        } catch (AlgorithmMismatchException e) {
            e.printStackTrace();
            map.put("msg","token算法不一致");
        } catch (Exception e) {
            e.printStackTrace();
            map.put("msg","token失效");
        }
        map.put("state",false);//設(shè)置狀態(tài)
        //將map轉(zhuǎn)化成json,response使用的是Jackson
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().print(json);
        return false;
    }
}

6.3.配置攔截器

package com.liup.config;
import com.office.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/**");
    }
}

到此這篇關(guān)于Java中的JWT使用詳解的文章就介紹到這了,更多相關(guān)Java中的JWT內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論