一架梯子,一头程序猿,仰望星空!

Golang JWT使用例子

如何在Go语言环境中使用JWT进行安全验证,涵盖安装、生成简易token及含自定义参数的token,token的解析与验证。

JWT介绍

JWT (JSON Web Token) 是一种在网络上用于身份验证的简洁、自包含的方法。它是由三部分组成的一个JSON对象,分别是头部(Header)、载荷(Payload)以及签名(Signature)。头部包含了令牌的类型和加密算法信息,载荷则包含了所需要传递的数据(通常包括一些权限信息和用户标识),签名用于验证令牌的完整性和有效性。

JWT主要用于两个场景:

  1. 认证:一旦用户登录,每个后续请求将包括JWT,允许用户访问允许的路由、服务和资源。
  2. 信息交换:JWT是在各方之间安全传输信息的好方法,因为它们可以进行数字签名,例如使用公钥/私钥对。

JWT数据格式

JWT 是一种紧凑的、URL安全的方式来表示要在双方之间传递的信息。一个JWT实际上由三个部分组成,它们之间用点(.)分隔,即Header.Payload.Signature。接下来,我们将详细介绍这三个部分的数据格式。

1. Header(头部)

头部通常由两部分组成:令牌的类型—通常是JWT,和所用的签名或加密算法,例如HMAC SHA256RSA。头部用JSON表示,然后用Base64Url编码成字符串。例如:

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

编码后可能会得到类似于这样的字符串:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

2. Payload(负载)

负载部分是由一系列的声明构成,声明是关于实体(通常是用户)和其他数据的信息。负载可以包含多个预定义的声明(也被称为Registered Claims),以及自定义的声明(Private Claims)。

预定义的声明可能会包括:

  • iss(Issuer):签发者
  • exp(Expiration Time):过期时间
  • sub(Subject):主题
  • aud(Audience):受众
  • iat(Issued At):签发时间
  • nbf(Not Before):生效时间
  • jti(JWT ID):JWT的唯一身份标识

一个例子负载可能看起来这样(以JSON格式表示):

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022
}

这些信息也将被Base64Url编码,可能会得到类似于这样的字符串:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0

3. Signature(签名)

签名部分是对以上两个编码后的字符串进行签名,以便验证消息在传递过程中没有被篡改。首先,您需要指定一个密钥(如果使用的是HMAC SHA256算法),然后使用Header中指定的算法对Header和Payload进行签名。

例如,如果您有以下的Header和Payload:

HeaderEncoded.HeaderPayload

使用密钥对它们进行签名的伪代码可能是:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secretKey)

签名后得到的字符串可能是:

dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY

完整的JWT

将这三部分合并,用点(.)分隔,就构成了完整的JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.dBjftJeZ4CVPmTaoyL4IiArYfL4kH0jOspm6XwbcJXY

JWT标准具有极大的灵活性,可以在Payload中存储您需要的任何信息(但为了减少JWT的大小和安全原因,不应该存储敏感信息),并且通过签名保证了这些信息的完整性。

安装Go JWT库

Go 社区 提供了一个包github.com/golang-jwt/jwt用于处理JWT。安装这个库非常简单,只需在项目目录下运行下列命令:

go get -u github.com/golang-jwt/jwt/v5

安装完成后,你可以在import中按如下方式包含它:

import "github.com/golang-jwt/jwt/v5"

创建简单的token

要使用Go语言和HS256算法创建一个简单的JWT token,你需要做以下步骤:

首先,生成一个新的Token对象,这里使用HS256算法:

token := jwt.New(jwt.SigningMethodHS256)

接下来,通过SignedString方法为这个token生成一个字符串形式的token,需要传递一个你用于签名的秘钥。

var mySigningKey = []byte("your-256-bit-secret")
strToken, err := token.SignedString(mySigningKey)
if err != nil {
    log.Fatalf("出现错误: %v", err)
}
fmt.Println(strToken)

这将会生成一个不包含任何Claims(声明)的简单token。

创建携带参数的token

JWT的一个主要功能是能够携带信息。这些信息通过claims在Token中编码。例如,我们创建自定义的claims:

// 自定义Claims结构体
type MyClaims struct {
	jwt.RegisteredClaims
	Username string `json:"username"`
	Admin    bool   `json:"admin"`
}

// 创建带有自定义Claims的token
claims := MyClaims{
    RegisteredClaims: jwt.RegisteredClaims{},
    Username: "my_username",
	Admin: true,
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

// 用于签名的秘钥
var mySigningKey = []byte("your-256-bit-secret")

// 生成字符串形式的token
strToken, err := token.SignedString(mySigningKey)
if err != nil {
    log.Fatalf("出现错误: %v", err)
}

fmt.Println(strToken)

在前面代码中,我们定义了MyClaims结构体来包含注册的claims以及一些自定义的信息。然后,我们依旧使用SignedString来生成token字符串。

解析和验证token

解析并验证JWT的有效性非常重要,你可以通过以下方式来进行:

// 我们将使用相同的MyClaims结构体
tokenString := "你的JWT字符串"

// 我们需要定义一个函数,jwt包会使用它来解析tokenString
keyFunc := func(t *jwt.Token) (interface{}, error) {
	// 确认使用的是你预期的签名算法
	if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
	    return nil, fmt.Errorf("意外的签名方法: %v", t.Header["alg"])
	}
	// 返回jwt token的秘钥,  格式 []byte, 跟前面签名使用的key保持一致
	return mySigningKey, nil
}

// 解析token
claims := &MyClaims{}
parsedToken, err := jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
	log.Fatalf("解析错误: %v", err)
}

if !parsedToken.Valid {
    log.Fatalf("无效Token")
}

// 在这一阶段, parsedToken已经经过验证,我们可以读取claims
fmt.Printf("User:%s,Admin:%v\n", claims.Username, claims.Admin)

在上述代码中,我们提供了token字符串、MyClaims实例和秘钥函数keyFuncjwt.ParseWithClaims函数。这个函数会验证签名并解析token,如果token有效,会填充claims变量。


章节目录