Go Gin 中使用 JWT

一、JWT

JWT全稱JSON Web Token是一種跨域認證解決方案,屬于一個開放的標準,它規定了一種Token實現方式,目前多用于前后端分離項目和OAuth2.0業務場景下。

二、為什么要用在你的Gin中使用JWT

傳統的Cookie-Sesson模式占用服務器內存, 拓展性不好,遇到集群或者跨服務驗證的場景的話, 要支持Sesson復制或者sesson持久化

1.JWT的基本原理

在服務器驗證之后, 得到用戶信息JSON

1

2

3

4

5

{

?????"user_id": "xxxxxx",

????"role": "xxxxxx",

????"refresh_token": "xxxxx"

}

(1)JWT TOKEN怎么組成

JWT是一個很長的字符串

eyJhbGciOiJI123afasrwrqqewrcCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

它由三部分組成, 每部分用點(.)分隔, 三個部分依次如下

?

  • Header(頭部)
  • Payload(負載)
  • Signature(簽名)

1)Header

Header是一個經過BASE64URL算法加密過的JSON對象, 解密后如下

1

2

3

4

{

??"alg": "HS256",

??"typ": "JWT"

}

其中,?alg屬性表示簽名所用的算法,默認是HS256;

typ則表示當前token的類型, 而JWT的類型則為jwt

Base64URL

與BASE64類似, 由于+、/、=這幾個符號在URL中有特殊含義, 因此BASE64RUL算法, 把這幾個符號進行了替換

+?->?-

=?-> 被忽略

/?->?_

2)Payload

Payload部分也是JSON對象經過BASE64URL算法轉成字符串的, Payload部分包含7個基本字段

  • iss (issuer):簽發人
  • exp (expiration time):過期時間
  • sub (subject):主題
  • aud (audience):受眾
  • nbf (Not Before):生效時間
  • iat (Issued At):簽發時間
  • jti (JWT ID):編號

也可以往里面塞入自定義的業務字段, 如下

user_id

role

3)Signature

Signature 部分是對前兩部分的簽名,防止數據篡改

首先,需要指定一個密鑰(secret)。這個密鑰只有服務器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產生簽名。

HMACSHA256(
? base64UrlEncode(header) + "." +
? base64UrlEncode(payload),
? secret)

(2)解密過程

當系統接收到TOKEN時, 拿出Header和Payload的字符串用.拼接在一起之后, 用Header里面指定的哈希方法通過公式

HMACSHA256(
? base64UrlEncode(header) + "." +
? base64UrlEncode(payload),
? secret)

進行加密得出密文

然后用剛剛得出的密文與TOKEN傳過來的密文對比, 如果相等則表明密文沒有更改.

三、JWT一些特點(優點與缺點)

  • JWT 默認是不加密,但也是可以加密的。生成原始 Token 以后,可以用密鑰再加密一次。
  • JWT 不加密的情況下,不能將秘密數據寫入 JWT。
  • JWT 不僅可以用于認證,也可以用于交換信息。有效使用 JWT,可以降低服務器查詢數據庫的次數。
  • JWT 的最大缺點是,由于服務器不保存 session 狀態,因此無法在使用過程中廢止某個 token,或者更改 token 的權限。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非服務器部署額外的邏輯。
  • JWT 本身包含了認證信息,一旦泄露,任何人都可以獲得該令牌的所有權限。為了減少盜用,JWT 的有效期應該設置得比較短。對于一些比較重要的權限,使用時應該再次對用戶進行認證。
  • 為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。

1.GIN整合JWT

1

2

go get -u github.com/dgrijalva/jwt-go

go get github.com/gin-gonic/gin

編寫jwtutil

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

var Secret = []byte("whatasecret")

// jwt過期時間, 按照實際環境設置

const expiration = 2 * time.Minute

type Claims struct {

????// 自定義字段, 可以存在用戶名, 用戶ID, 用戶角色等等

????Username string

????// jwt.StandardClaims包含了官方定義的字段

????jwt.StandardClaims

}

func GenToken(username string) (string, error) {

????// 創建聲明

????a := &Claims{

????????Username: username,

????????StandardClaims: jwt.StandardClaims{

????????????ExpiresAt: time.Now().Add(expiration).Unix(), // 過期時間

????????????IssuedAt:? time.Now().Unix(),???????????????? // 簽發時間

????????????Issuer:??? "gin-jwt-demo",??????????????????? // 簽發者

????????????Id:??????? "",??????????????????????????????? // 按需求選這個, 有些實現中, 會控制這個ID是不是在黑/白名單來判斷是否還有效

????????????NotBefore: 0,???????????????????????????????? // 生效起始時間

????????????Subject:?? "",??????????????????????????????? // 主題

????????},

????}

????// 用指定的哈希方法創建簽名對象

????tt := jwt.NewWithClaims(jwt.SigningMethodHS256, a)

????// 用上面的聲明和簽名對象簽名字符串token

????// 1. 先對Header和PayLoad進行Base64URL轉換

????// 2. Header和PayLoadBase64URL轉換后的字符串用.拼接在一起

????// 3. 用secret對拼接在一起之后的字符串進行HASH加密

????// 4. 連在一起返回

????return tt.SignedString(Secret)

}

func ParseToken(tokenStr string) (*Claims, error) {

????// 第三個參數: 提供一個回調函數用于提供要選擇的秘鑰, 回調函數里面的token參數,是已經解析但未驗證的,可以根據token里面的值做一些邏輯, 如`kid`的判斷

????token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {

????????return Secret, nil

????})

????if err != nil {

????????return nil, err

????}

????// 校驗token

????if claims, ok := token.Claims.(*Claims); ok && token.Valid {

????????return claims, nil

????}

????return nil, errors.New("invalid token")

}

  • Secret是秘鑰, 用于加密簽名
  • expiration是TOKEN過期時間
  • Claims是簽名聲明對象, 包含自定義的字段和JWT規定的字段

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

type Claims struct {

????// 自定義字段, 可以存在用戶名, 用戶ID, 用戶角色等等

????Username string

????// jwt.StandardClaims包含了官方定義的字段

????jwt.StandardClaims

}

type StandardClaims struct {

????Audience? string `json:"aud,omitempty"`

????ExpiresAt int64? `json:"exp,omitempty"`

????Id??????? string `json:"jti,omitempty"`

????IssuedAt? int64? `json:"iat,omitempty"`

????Issuer??? string `json:"iss,omitempty"`

????NotBefore int64? `json:"nbf,omitempty"`

????Subject?? string `json:"sub,omitempty"`

}

(1)GenToken方法

GenToken方法為某個username生成一個token, 每次生成都不一樣

jwt.NewWithClaims(jwt.SigningMethodHS256, a)聲明了一個簽名對象, 并且指定了HS256的哈希算法

token.SignedString(Secret)表明用剛剛的聲明對象和SECRET利用指定的哈希算法去加密,包含下面流程

  • 先對Header和PayLoad進行Base64URL轉換
  • Header和PayLoadBase64URL轉換后的字符串用.拼接在一起
  • 用secret對拼接在一起之后的字符串進行HASH加密
  • BASE64URL(Header).BASE64URL(Payload).signature連在一起的字符串返回

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

func GenToken(username string) (string, error) {

????// 創建聲明

????a := &Claims{

????????Username: username,

????????StandardClaims: jwt.StandardClaims{

????????????ExpiresAt: time.Now().Add(expiration).Unix(), // 過期時間

????????????IssuedAt:? time.Now().Unix(),???????????????? // 簽發時間

????????????Issuer:??? "gin-jwt-demo",??????????????????? // 簽發者

????????????Id:??????? "",??????????????????????????????? // 按需求選這個, 有些實現中, 會控制這個ID是不是在黑/白名單來判斷是否還有效

????????????NotBefore: 0,???????????????????????????????? // 生效起始時間

????????????Subject:?? "",??????????????????????????????? // 主題

????????},

????}

????// 用指定的哈希方法創建簽名對象

????tt := jwt.NewWithClaims(jwt.SigningMethodHS256, a)

????// 用上面的聲明和簽名對象簽名字符串token

????// 1. 先對Header和PayLoad進行Base64URL轉換

????// 2. Header和PayLoadBase64URL轉換后的字符串用.拼接在一起

????// 3. 用secret對拼接在一起之后的字符串進行HASH加密

????// 4. 連在一起返回

????return tt.SignedString(Secret)

}

(2)ParseToken方法

ParseToken方法解析一個Token, 并驗證Token是否生效

jwt.ParseWithClaims方法, 用于解析Token, 其第三個參數:

提供一個回調函數用于提供要選擇的秘鑰, 回調函數里面的token參數,是已經解析但未驗證的,可以根據token里面的值做一些邏輯, 如判斷kid來選用不同的secret

KID(可選): 代表秘鑰序號。開發人員可以用它標識認證token的某一秘鑰

1

2

3

4

5

6

7

8

9

10

11

12

13

14

func ParseToken(tokenStr string) (*Claims, error) {

????// 第三個參數: 提供一個回調函數用于提供要選擇的秘鑰, 回調函數里面的token參數,是已經解析但未驗證的,可以根據token里面的值做一些邏輯, 如`kid`的判斷

????token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {

????????return Secret, nil

????})

????if err != nil {

????????return nil, err

????}

????// 校驗token

????if claims, ok := token.Claims.(*Claims); ok && token.Valid {

????????return claims, nil

????}

????return nil, errors.New("invalid token")

}

編寫中間件

從Header中取出Authorization并拿去解析jwt.ParseToken,

驗證token是否被串改, 是否過期

從token取出有效信息并設置到上下文

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

func JWTAuthMiddleware() func(ctx *gin.Context) {

????return func(ctx *gin.Context) {

????????// 根據實際情況取TOKEN, 這里從request header取

????????tokenStr := ctx.Request.Header.Get("Authorization")

????????if tokenStr == "" {

????????????ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{

????????????????"code": code.ERR_AUTH_NULL,

????????????????"msg":? code.GetMsg(code.ERR_AUTH_NULL),

????????????})

????????????return

????????}

????????claims, err := jwt.ParseToken(tokenStr)

????????if err != nil {

????????????ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{

????????????????"code": code.ERR_AUTH_INVALID,

????????????????"msg":? code.GetMsg(code.ERR_AUTH_INVALID),

????????????})

????????????return

????????} else if time.Now().Unix() > claims.ExpiresAt {

????????????ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{

????????????????"code": code.ERR_AUTH_EXPIRED,

????????????????"msg":? code.GetMsg(code.ERR_AUTH_EXPIRED),

????????????})

????????????return

????????}

????????// 此處已經通過了, 可以把Claims中的有效信息拿出來放入上下文使用

????????ctx.Set("username", claims.Username)

????????ctx.Next()

????}

}

使用中間件

/login不用中間件

中間件指定在authorizedrouter, 因此authorized下的所有路由都會使用此中間件

1

2

3

4

5

6

7

8

9

10

func main() {

????r := gin.Default()

????r.POST("/login", router.Login)

????authorized := r.Group("/auth")

????authorized.Use(jwt.JWTAuthMiddleware())

????{

????????authorized.GET("/getUserInfo", router.GetUserInfo)

????}

????r.Run(":8082")

}

測試

login請求獲取token

POST?http://localhost:8082/login

?

把token放入getUserInfo請求

GET? http://localhost:8082/auth/getUserInfo

?

其他

完整的JWT登錄還應該包括

  • 使TOKEN失效(過期或者黑名單等功能)
  • refresh token

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35467.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35467.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35467.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

uniapp實現自定義導航內容高度居中(兼容APP端以及小程序端與膠囊對齊)

①效果圖如下 1.小程序端與膠囊對齊 2.APP端內容區域居中 注意:上面使用的是colorui里面的自定義導航樣式。 ②思路: 1.APP端和小程序端走不同的方法,因為小程序端要計算不同屏幕下右側膠囊的高度。 2.其次最重要的要清晰App端和小程序端…

【數學建模】清風數模更新5 灰色關聯分析

灰色關聯分析綜述 諸如經濟系統、生態系統、社會系統等抽象系統都包含許多因素,系統整體的發展受各個因素共同影響。 為了更好地推動系統發展,我們需要清楚哪些因素是主要的,哪些是次要的,哪些是積極的,哪些是消極的…

網絡基礎——網絡的由來與發展史

作者:Insist-- 個人主頁:insist--個人主頁 作者會持續更新網絡知識和python基礎知識,期待你的關注 目錄 一、網絡的由來 二、計算機網絡的發展史 1、第一階段 2、第二階段 3、第三階段 前言 每天都是使用網絡,那么你知道網絡…

FPGA----Vivado SDK創建并使用靜態鏈接庫(C/C++代碼移植)

1、在進行SoC開發時,PS端的C/C代碼可能涉及到核心算法需要移植操作,為此,本文講述了如何將C/C代碼打包為.a文件供程序調用 2、文章以我的程序為例,逐步講述代碼生成靜態鏈接庫并調用的方法。 下面是我程序的目錄結構&#xff0c…

spring boot實現實體類參數自定義校驗

安裝依賴項 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>1、新建實體類 Data public class UserEntity {private String name;private Integer age;…

RocketMQ 延遲消息

RocketMQ 延遲消息 RocketMQ 消費者啟動流程 什么是延遲消息 RocketMQ 延遲消息是指&#xff0c;生產者發送消息給消費者消息&#xff0c;消費者需要等待一段時間后才能消費到。 使用場景 用戶下單之后&#xff0c;15分鐘未支付&#xff0c;對支付賬單進行提醒或者關單處理…

PostgreSQL查詢慢sql原因和優化方案

PostgreSQL sql查詢慢優化方案有一下幾種解決方案&#xff1a; 1.關閉會話 查詢慢sql的執行會話&#xff0c;關閉進程。 查看數據庫后臺連接進程 SELECT count(*) FROM pg_stat_activity;SELECT * FROM pg_stat_activity; 查看數據庫后臺連接進程&#xff0c;但是此條SQL不…

python提取pdf圖片

import fitz import re import osdef save_pdf_img(path, save_path):path: pdf的路徑save_path : 圖片存儲的路徑# 使用正則表達式來查找圖片checkXO r"/Type(? */XObject)"checkIM r"/Subtype(? */Image)"# 打開pdfdoc fitz.open(path)# 圖片計數im…

用HARU-Net增強核分割:一種基于混合注意的殘差u塊網絡

文章目錄 Enhancing Nucleus Segmentation with HARU-Net: A Hybrid Attention Based Residual U-Blocks Network摘要本文方法損失函數后處理消融實驗 Enhancing Nucleus Segmentation with HARU-Net: A Hybrid Attention Based Residual U-Blocks Network 摘要 核圖像分割是…

W6100-EVB-PICO 做TCP Server進行回環測試(六)

前言 上一章我們用W6100-EVB-PICO開發板做TCP 客戶端連接服務器進行數據回環測試&#xff0c;那么本章將用開發板做TCP服務器來進行數據回環測試。 TCP是什么&#xff1f;什么是TCP Server&#xff1f;能干什么&#xff1f; TCP (Transmission Control Protocol) 是一種面向連…

zabbix監控安裝部署

目錄 一、環境 二、配置 1.配置yum源&#xff0c;這里用的清華的 2.過濾一下安裝包&#xff0c;查看依賴包 安裝依賴包 3.配置數據庫 開機自啟 創建數據庫 創建用戶 授權 導入數據到數據庫 查看zabbix數據庫有沒有表和數據 4.修改zabbix配置文件 1.修改zabbix配置…

去趨勢化一個心電圖信號、信號功率譜、低通IIR濾波器并平滑信號、對濾波器引起的延遲進行補償研究(Matlab代碼實現)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;歡迎來到本博客????&#x1f4a5;&#x1f4a5; &#x1f3c6;博主優勢&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客內容盡量做到思維縝密&#xff0c;邏輯清晰&#xff0c;為了方便讀者。 ??座右銘&a…

SPM實現framework自動管理和分發

一、前言 Swift Package Manager (SPM) 是蘋果官方提供的用于管理 Swift 項目的依賴關系和構建過程的工具。它是一個集成在 Swift 編程語言中的包管理器&#xff0c;用于解決在開發過程中管理和構建包依賴項的需求。 那么如何使用SPM管理和分發Objective C編寫的二進制庫呢&a…

HOT86-單詞拆分

leetcode原題鏈接&#xff1a;單詞拆分 題目描述 給你一個字符串 s 和一個字符串列表 wordDict 作為字典。請你判斷是否可以利用字典中出現的單詞拼接出 s 。注意&#xff1a;不要求字典中出現的單詞全部都使用&#xff0c;并且字典中的單詞可以重復使用。 示例 1&#xff1a…

不同路徑 II——力扣63

class Solution {public:int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {int n=

一鍵登錄是如何在登錄方式中脫穎而出的?

首先&#xff0c;我們先了解一下登錄方式的演變過程&#xff0c;大致可以分為三個階段。分別是賬號密碼登錄、短信驗證碼登錄和一鍵登錄。 階段一&#xff1a;賬號密碼登錄 賬號密碼登錄是一種常見的用戶身份驗證方式&#xff0c;用戶需要輸入一個唯一的賬號和對應的密碼來登…

【APITable】教程:創建并運行一個自建小程序

1.進入APITable&#xff0c;在想要創建小程序的看板頁面點擊右上角的【小程序】&#xff0c;進入小程序編輯頁面。 2.創建一個新的小程序區。 點擊【 添加小程序】 點擊創建小程序&#xff0c;選擇模板&#xff0c;輸入名字。 3.確定后進入小程序部署引導頁面。 4.打開Xshell 7…

初識鴻蒙跨平臺開發框架ArkUI-X

HarmonyOS是一款面向萬物互聯時代的、全新的分布式操作系統。在傳統的單設備系統能力基礎上&#xff0c;HarmonyOS提出了基于同一套系統能力、適配多種終端形態的分布式理念&#xff0c;能夠支持手機、平板、智能穿戴、智慧屏、車機等多種終端設備&#xff0c;提供全場景&#…

99. for循環練習題-3種方式輸出0-9

【目錄】 文章目錄 99. for循環練習題-3種方式輸出0-91. for循環和while循環的區別2. 輸出 0~(n-1)的數字2.1 基礎代碼2.2 自定義函數代碼2.3 異常處理語句代碼 【正文】 99. for循環練習題-3種方式輸出0-9 1. for循環和while循環的區別 for循環和while循環都用于重復執行特定…

Linux一些常見的命令

1. 基礎命令 1. ls&#xff1a; 列出目錄內容。- 例如&#xff1a;ls -l 以長格式列出文件和目錄。2. cd&#xff1a; 切換工作目錄。- 例如&#xff1a;cd /home/user 進入 /home/user 目錄。3. pwd&#xff1a; 顯示當前工作目錄的路徑。4. mkdir&#xff1a; 創建新目錄。-…