亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文了解什么是JWT

 更新時間:2023年05月09日 14:20:23   作者:java技術(shù)愛好者  
JSON?WEB?Token是一種基于JSON的、用于在網(wǎng)絡(luò)上聲明某種主張的令牌,由三部分組成:?頭信息,?消息體和簽名,下面就一起來了解一下什么是JWT

起源

需要了解一門技術(shù),首先從為什么產(chǎn)生開始說起是最好的。JWT 主要用于用戶登錄鑒權(quán),所以我們從最傳統(tǒng)的 session 認(rèn)證開始說起。

session認(rèn)證

眾所周知,http 協(xié)議本身是無狀態(tài)的協(xié)議,那就意味著當(dāng)有用戶向系統(tǒng)使用賬戶名稱和密碼進(jìn)行用戶認(rèn)證之后,下一次請求還要再一次用戶認(rèn)證才行。因?yàn)槲覀儾荒芡ㄟ^ http 協(xié)議知道是哪個用戶發(fā)出的請求,所以如果要知道是哪個用戶發(fā)出的請求,那就需要在服務(wù)器保存一份用戶信息(保存至 session ),然后在認(rèn)證成功后返回 cookie 值傳遞給瀏覽器,那么用戶在下一次請求時就可以帶上 cookie 值,服務(wù)器就可以識別是哪個用戶發(fā)送的請求,是否已認(rèn)證,是否登錄過期等等。這就是傳統(tǒng)的 session 認(rèn)證方式。

session 認(rèn)證的缺點(diǎn)其實(shí)很明顯,由于 session 是保存在服務(wù)器里,所以如果分布式部署應(yīng)用的話,會出現(xiàn)session不能共享的問題,很難擴(kuò)展。于是乎為了解決 session 共享的問題,又引入了 redis,接著往下看。

token認(rèn)證

這種方式跟 session 的方式流程差不多,不同的地方在于保存的是一個 token 值到 redis,token 一般是一串隨機(jī)的字符(比如UUID),value 一般是用戶ID,并且設(shè)置一個過期時間。每次請求服務(wù)的時候帶上 token 在請求頭,后端接收到token 則根據(jù) token 查一下 redis 是否存在,如果存在則表示用戶已認(rèn)證,如果 token 不存在則跳到登錄界面讓用戶重新登錄,登錄成功后返回一個 token 值給客戶端。

優(yōu)點(diǎn)是多臺服務(wù)器都是使用 redis 來存取 token,不存在不共享的問題,所以容易擴(kuò)展。缺點(diǎn)是每次請求都需要查一下redis,會造成 redis 的壓力,還有增加了請求的耗時,每個已登錄的用戶都要保存一個 token 在 redis,也會消耗 redis 的存儲空間。

有沒有更好的方式呢?接著往下看。

什么是JWT

JWT (全稱:Json Web Token)是一個開放標(biāo)準(zhǔn)(RFC 7519),它定義了一種緊湊的、自包含的方式,用于作為 JSON 對象在各方之間安全地傳輸信息。該信息可以被驗(yàn)證和信任,因?yàn)樗菙?shù)字簽名的。

上面說法比較文縐縐,簡單點(diǎn)說就是一種認(rèn)證機(jī)制,讓后臺知道該請求是來自于受信的客戶端。

首先我們先看一個流程圖:

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-7UDQ9SEm-1653810812356)(什么是JWT.assets/v2-bd0aeaf5ba1bad5ab1edff601e9b7a78_720w.jpg)]

流程描述一下:

  • 用戶使用賬號、密碼登錄應(yīng)用,登錄的請求發(fā)送到 Authentication Server。
  • Authentication Server 進(jìn)行用戶驗(yàn)證,然后創(chuàng)建 JWT 字符串返回給客戶端。
  • 客戶端請求接口時,在請求頭帶上 JWT。
  • Application Server 驗(yàn)證 JWT 合法性,如果合法則繼續(xù)調(diào)用應(yīng)用接口返回結(jié)果。

可以看出與token方式有一些不同的地方,就是不需要依賴 redis,用戶信息存儲在客戶端。所以關(guān)鍵在于生成 JWT 和解析 JWT 這兩個地方。

JWT的數(shù)據(jù)結(jié)構(gòu)

JWT 一般是這樣一個字符串,分為三個部分,以 “.” 隔開:

xxxxx.yyyyy.zzzzz

Header

JWT 第一部分是頭部分,它是一個描述 JWT 元數(shù)據(jù)的 Json 對象,通常如下所示。

{
    "alg": "HS256",
    "typ": "JWT"
}

alg 屬性表示簽名使用的算法,默認(rèn)為 HMAC SHA256(寫為HS256),typ 屬性表示令牌的類型,JWT 令牌統(tǒng)一寫為JWT。

最后,使用 Base64 URL 算法將上述 JSON 對象轉(zhuǎn)換為字符串保存。

Payload

JWT 第二部分是 Payload,也是一個 Json 對象,除了包含需要傳遞的數(shù)據(jù),還有七個默認(rèn)的字段供選擇。

  • iss (issuer):簽發(fā)人/發(fā)行人
  • sub (subject):主題
  • aud (audience):用戶
  • exp (expiration time):過期時間
  • nbf (Not Before):生效時間,在此之前是無效的
  • iat (Issued At):簽發(fā)時間
  • jti (JWT ID):用于標(biāo)識該 JWT

如果自定義字段,可以這樣定義:

{
    //默認(rèn)字段
    "sub":"主題123",
    //自定義字段
    "name":"java技術(shù)愛好者",
    "isAdmin":"true",
    "loginTime":"2021-12-05 12:00:03"
}

需要注意的是,默認(rèn)情況下 JWT 是未加密的,任何人都可以解讀其內(nèi)容,因此一些敏感信息不要存放于此,以防信息泄露。

JSON 對象也使用 Base64 URL 算法轉(zhuǎn)換為字符串后保存,是可以反向反編碼回原樣的,這也是為什么不要在 JWT 中放敏感數(shù)據(jù)的原因。

Signature

header (base64URL 加密后的)
payload (base64URL 加密后的)
secret

JWT 第三部分是簽名。是這樣生成的,首先需要指定一個 secret,該 secret 僅僅保存在服務(wù)器中,保證不能讓其他用戶知道。這個部分需要 base64URL 加密后的 header 和 base64URL 加密后的 payload 使用 . 連接組成的字符串,然后通過header 中聲明的加密算法 進(jìn)行加鹽secret組合加密,然后就得出一個簽名哈希,也就是Signature,且無法反向解密。

那么 Application Server 如何進(jìn)行驗(yàn)證呢?可以利用 JWT 前兩段,用同一套哈希算法和同一個 secret 計算一個簽名值,然后把計算出來的簽名值和收到的 JWT 第三段比較,如果相同則認(rèn)證通過。

JWT的優(yōu)點(diǎn)

  • json格式的通用性,所以JWT可以跨語言支持,比如Java、JavaScript、PHP、Node等等。
  • 可以利用Payload存儲一些非敏感的信息。
  • 便于傳輸,JWT結(jié)構(gòu)簡單,字節(jié)占用小。
  • 不需要在服務(wù)端保存會話信息,易于應(yīng)用的擴(kuò)展。

怎么使用JWT

首先引入Maven依賴。

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

創(chuàng)建工具類,用于創(chuàng)建(生成) jwt 字符串和解析 jwt。

@Component
public class JwtUtil {
    @Value("${jwt.secretKey}")
    private String secretKey;
    public String createJWT(String id, String subject, long ttlMillis, Map<String, Object> map) throws Exception {
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject) // 發(fā)行者
                .setIssuedAt(new Date()) // 發(fā)行時間
                .signWith(SignatureAlgorithm.HS256, secretKey) // 簽名類型 與 密鑰
                .compressWith(CompressionCodecs.DEFLATE);// 對載荷進(jìn)行壓縮
        if (!CollectionUtils.isEmpty(map)) {
            builder.setClaims(map);
        }
        if (ttlMillis > 0) {
            builder.setExpiration(new Date(System.currentTimeMillis() + ttlMillis));
        }
        return builder.compact();
    }
    public Claims parseJWT(String jwtString) {
        return Jwts.parser().setSigningKey(secretKey)
                .parseClaimsJws(jwtString)
                .getBody();
    }
}

接著在application.yml配置文件配置jwt.secretKey。

## 用戶生成jwt字符串的secretKey
jwt:
  secretKey: ak47

接著創(chuàng)建一個響應(yīng)體。

public class BaseResponse {
    private String code;
    private String msg;
    public static BaseResponse success() {
        return new BaseResponse("0", "成功");
    }
    public static BaseResponse fail() {
        return new BaseResponse("1", "失敗");
    }
    //構(gòu)造器、getter、setter方法
}
public class JwtResponse extends BaseResponse {
    private String jwtData;
    public static JwtResponse success(String jwtData) {
        BaseResponse success = BaseResponse.success();
        return new JwtResponse(success.getCode(), success.getMsg(), jwtData);
    }
    public static JwtResponse fail(String jwtData) {
        BaseResponse fail = BaseResponse.fail();
        return new JwtResponse(fail.getCode(), fail.getMsg(), jwtData);
    }
    //構(gòu)造器、getter、setter方法
}

接著創(chuàng)建一個UserController:

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserService userService;

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public JwtResponse login(@RequestParam(name = "userName") String userName,
                             @RequestParam(name = "passWord") String passWord){
        String jwt = "";
        try {
            jwt = userService.login(userName, passWord);
            return JwtResponse.success(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            return JwtResponse.fail(jwt);
        }
    }
}

還有UserService:

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private UserMapper userMapper;

    @Override
    public String login(String userName, String passWord) throws Exception {
        //登錄驗(yàn)證
        User user = userMapper.findByUserNameAndPassword(userName, passWord);
        if (user == null) {
            return null;
        }
        //如果能查出,則表示賬號密碼正確,生成jwt返回
        String uuid = UUID.randomUUID().toString().replace("-", "");
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", user.getName());
        map.put("age", user.getAge());
        return jwtUtil.createJWT(uuid, "login subject", 0L, map);
    }
}

還有UserMapper.xml:

@Mapper
public interface UserMapper {
    User findByUserNameAndPassword(@Param("userName") String userName, @Param("passWord") String passWord);

}

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.github.yehongzhi.jwtdemo.mapper.UserMapper">
    <select id="findByUserNameAndPassword" resultType="io.github.yehongzhi.jwtdemo.model.User">
        select * from user where user_name = #{userName} and pass_word = #{passWord}
    </select>
</mapper>

user 表結(jié)構(gòu)如下:

啟動項(xiàng)目,然后用 postman 請求 login 接口。

返回的 jwt 字符串如下:

eyJhbGciOiJIUzI1NiIsInppcCI6IkRFRiJ9.eNqqVspLzE1VslJ6OnHFsxnzX67coKSjlJgOFDEzqAUAAAD__w.qib2DrjRKcFnY77Cuh_b1zSzXfISOpCA-g8PlAZCWoU

接著我們寫一個接口接收這個 jwt,并做驗(yàn)證。

@RestController
@RequestMapping("/jwt")
public class TestController {
    @Resource
    private JwtUtil jwtUtil;
    @RequestMapping("/test")
    public Map<String, Object> test(@RequestParam("jwt") String jwt) {
        //這個步驟可以使用自定義注解+AOP編程做解析jwt的邏輯,這里為了簡便就直接寫在controller里
        Claims claims = jwtUtil.parseJWT(jwt);
        String name = claims.get("name", String.class);
        String age = claims.get("age", String.class);
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("age", age);
        map.put("code", "0");
        map.put("msg", "請求成功");
        return map;
    }
}

像這樣能正常解析成功的話,就表示該用戶登錄未過期,并且已認(rèn)證成功,所以可以正常調(diào)用服務(wù)。那么有人會問了,這個 jwt 字符串能不能被偽造呢?

除非你知道 secretKey,否則是不能偽造的。比如客戶端隨便猜一個 secretKey 的值,然后偽造一個jwt:

eyJhbGciOiJIUzI1NiIsInppcCI6IkRFRiJ9.eNqqVspLzE1VslJ6OnHFsxnzX67coKSjlJgOFDEzqAUAAAD__w.bHr9p3-t2qR4R50vifRVyaYYImm2viZqiTlDdZHmF5Y

然后傳進(jìn)去解析,會報以下錯誤:

還記得原理吧,是根據(jù)前面兩部分(Header、Payload)加上 secretKey 使用 Header 指定的哈希算法計算出第三部分(Signature),所以可以看出最關(guān)鍵就是 secretKey。secretKey只有服務(wù)端自己知道,所以客戶端不知道 secretKey 的值是偽造不了jwt字符串的。

總結(jié)

最后講講 JWT 的缺點(diǎn),因?yàn)槿魏渭夹g(shù)都不是完美的,所以我們得用辯證思維去看待任何一項(xiàng)技術(shù)。

安全性沒法保證,所以 jwt 里不能存儲敏感數(shù)據(jù)。因?yàn)?jwt 的 payload 并沒有加密,只是用 Base64 編碼而已。無法中途廢棄。因?yàn)橐坏┖灠l(fā)了一個 jwt,在到期之前始終都是有效的,如果用戶信息發(fā)生更新了,只能等舊的 jwt 過期后重新簽發(fā)新的 jwt。續(xù)簽問題。當(dāng)簽發(fā)的 jwt 保存在客戶端,客戶端一直在操作頁面,按道理應(yīng)該一直為客戶端續(xù)長有效時間,否則當(dāng) jwt有效期到了就會導(dǎo)致用戶需要重新登錄。那么怎么為 jwt 續(xù)簽?zāi)??最簡單粗暴就是每次簽發(fā)新的 jwt,但是由于過于暴力,會影響性能。如果要優(yōu)雅一點(diǎn),又要引入 Redis 解決,但是這又把無狀態(tài)的 jw t硬生生變成了有狀態(tài)的,違背了初衷。

所以印證了那句話,沒有最好的技術(shù),只有適合的技術(shù)。

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

相關(guān)文章

  • JavaScript 解析Json字符串的性能比較分析代碼

    JavaScript 解析Json字符串的性能比較分析代碼

    我們在使用AJAX來做服務(wù)器端和客戶端交互的時候,一般的做法是讓服務(wù)器端返回一段JSON字符串,然后在客戶端把它解析成JavaScript對象。
    2009-12-12
  • json數(shù)據(jù)傳到前臺并解析展示成列表的方法

    json數(shù)據(jù)傳到前臺并解析展示成列表的方法

    今天小編就為大家分享一篇json數(shù)據(jù)傳到前臺并解析展示成列表的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • JSON 數(shù)據(jù)格式詳解

    JSON 數(shù)據(jù)格式詳解

    JSON(JavaScript Object Notation) 是一種輕量級的數(shù)據(jù)交換格式。JSON采用完全獨(dú)立于語言的文本格式,這些特性使JSON成為理想的數(shù)據(jù)交換語言。易于人閱讀和編寫,同時也易于機(jī)器解析和生成
    2017-09-09
  • json的定義、標(biāo)準(zhǔn)格式及json字符串檢驗(yàn)

    json的定義、標(biāo)準(zhǔn)格式及json字符串檢驗(yàn)

    今天分享和總結(jié)一些json的基本定義、格式、字符串的格式,以及在做測試的時候使用json時做一些簡單的校驗(yàn)
    2014-05-05
  • Javascript Jquery 遍歷Json的實(shí)現(xiàn)代碼

    Javascript Jquery 遍歷Json的實(shí)現(xiàn)代碼

    Javascript Jquery 遍歷Json的實(shí)現(xiàn)代碼,需要的朋友可以參考下。
    2010-03-03
  • JSON相關(guān)知識匯總

    JSON相關(guān)知識匯總

    本文給大家匯總了一下關(guān)于json的相關(guān)的知識點(diǎn),從基礎(chǔ)到示例,非常全面,有需要的小伙伴可以參考下。
    2015-07-07
  • ASP Json Parser修正版

    ASP Json Parser修正版

    之前因?yàn)橐胘son,在網(wǎng)上,json Generator就不少,但是,parser鮮有后來,在一個老外的啟發(fā)下,寫了一個praser,其實(shí)超簡單,就是利用了JS的eval來parse,然后,把對象再返回給vbscript代碼。
    2009-12-12
  • JS解析后臺返回的JSON格式數(shù)據(jù)實(shí)例

    JS解析后臺返回的JSON格式數(shù)據(jù)實(shí)例

    今天小編就為大家分享一篇JS解析后臺返回的JSON格式數(shù)據(jù)實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Javascript 通過json自動生成Dom的代碼

    Javascript 通過json自動生成Dom的代碼

    主要還是通過遞歸和迭代來遍歷json成員生成html元素 ,比較好的是num能制定循環(huán)次數(shù)可以少寫很多代碼.具體應(yīng)用看場景了
    2010-04-04
  • js中將字符串轉(zhuǎn)換成json的三種方式

    js中將字符串轉(zhuǎn)換成json的三種方式

    使用ajax的開發(fā)項(xiàng)目過程中,經(jīng)常需要將json格式的字符串返回到前端,前端解析成js對象(JSON )。
    2011-01-01

最新評論