boxmoe_header_banner_img

Hello! 欢迎来到我的博客!

加载中

文章导读

JWT鉴权


avatar
xiaoifei 2026年7月2日 4

初识JWT

JWT(JSON Web Token)是基于 JSON 的开放标准(RFC 7519),用于在各方之间安全地传递信息。

它由三部分组成:

部分用途示例内容
Header(头部)指定算法和类型{“alg”:”HS256″,”typ”:”JWT”}
Payload(有效载荷)存放声明(Claims),如用户 ID、过期时间等{“sub”:”1234567890″,”name”:”Alice”,”exp”:1600000000}
Signature(签名)保证前两部分不被篡改HMACSHA256(Base64Url(Header) + “.” + Base64Url(Payload), Secret)

Header

Header(头部)通常由两部分组成,分别是令牌的类型和所使用的签名算法(HMAC SHA256、RSA 等),其会组成一个 JSON 对象用于描述其元数据,例如:

{
  "alg": "HS256",
  "typ": "JWT"
}
  • alg 字段表示签名算法,默认是 HMAC SHA256(HS256)
  • type 字段表示令牌类型,我们使用的 JWT 令牌类型,在最后会对上面的 JSON 对象进行 base64UrlEncode 算法进行转换成为 JWT 的第一部分。

Base64UrlEncode 是 Base64 算法的变种,在 URL 中,一些个别字符是有特殊意义的,例如:“+”、“/”、“=” 等等,因此在 Base64UrlEncode 算法中,会对其进行替换,例如:“+” 替换为 “-”“/”替换成 “_”“=” 会被进行忽略处理,以此来保证 JWT 令牌的在 URL 中的可用性和准确性。

Payload

Payload(有效载荷)存储实际传输的数据

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

Payload可以包含自定义字段和通用标准字段

JWT通用标准字段如下

  • aud(Audience):受众,也就是接受 JWT 的一方。
  • exp(ExpiresAt):所签发的 JWT 过期时间,过期时间必须大于签发时间。
  • jti(JWT Id):JWT 的唯一标识。
  • iat(IssuedAt):签发时间
  • iss(Issuer):JWT 的签发者。
  • nbf(Not Before):JWT 的生效时间,如果未到这个时间则为不可用。
  • sub(Subject):主题

同样也会对该 JSON 对象进行 base64UrlEncode 算法将其转换为 JWT Token 的第二部分。
注意:敏感信息请不要放到 JWT 中,因为他是可逆的,能够被读取

Signature

Signature(签名)部分是对前面两个部分组合(Header+Payload)进行约定算法和规则的签名,而签名将会用于校验消息在整个过程中有没有被篡改,并且对有使用私钥进行签名的令牌,它还可以验证 JWT 的发送者是否它的真实身份。

在签名的生成上,在应用程序指定了 密钥(secret) 后,会使用传入的指定签名算法(默认是 HMAC SHA256),然后通过下述的签名方式来完成 Signature(签名)部分的生成,如下:

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

因此我们可以看出 JWT 的第三部分是由 Header、Payload 以及 Secret 的算法组成而成的,因此它最终可达到用于校验消息是否被篡改的作用之一,因为如果一旦被篡改,Signature 就会无法对上


简单使用

Go

Go提供了社区维护的JWT仓库
使用命令:go get github.com/golang-jwt/jwt/v5获取

基本用法案例

type Claims struct {
	AppKey    string `json:"app_key"`
	AppSecret string `json:"app_secret"`
	jwt.RegisteredClaims
}

func GetJWTSecret() []byte {
	return []byte(global.JWTSetting.Secret)
}

func GenerateToken(appKey, appSecret string) (string, error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(global.JWTSetting.Expire)
	claims := Claims{
		AppKey:    utils.EncodeMD5(appKey),
		AppSecret: utils.EncodeMD5(appSecret),
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expireTime),
			Issuer:    global.JWTSetting.Issuer,
		},
	}

	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenClaims.SignedString(GetJWTSecret())
	return token, err
}

func ParseToken(token string) (*Claims, error) {
	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		return GetJWTSecret(), nil
	})
	if err != nil {
		return nil, err
	}
	if tokenClaims != nil {
		if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
			return claims, nil
		}
	}

	return nil, err
}

我们声明的Claims即代表Payload有效载荷
其中可以定义我们自己的业务字段,例如携带用户id等。还有官方标准规范(RFC 7519)中定义的一组通用标准字段

type RegisteredClaims struct {
	Issuer    string         `json:"iss,omitempty"`
	Subject   string         `json:"sub,omitempty"`
	Audience  ClaimStrings   `json:"aud,omitempty"`
	ExpiresAt *NumericDate   `json:"exp,omitempty"`
	NotBefore *NumericDate   `json:"nbf,omitempty"`
	IssuedAt  *NumericDate   `json:"iat,omitempty"`
	ID        string         `json:"jti,omitempty"`
}

其中jwt.ParseWithClaims在解析时字段校验规则不同

  • 他会对时间敏感字段的默认校验(如 exp, nbfiat等),例如ExpiresAt存在时会默认对其进行校验
  • 而对于身份与空间隔离字段(如 iss, aud等),即使写了也不会默认校验,要么在解密函数中进行校验(在函数后加上jwt.WithIssuer(global.JWTSetting.Issuer)的配置项),要么解密后进行校验

附录

JWT通用标准字段

  1. Issuer (iss) — 签发者
    作用:标记这个 Token 是由哪个服务器、哪个系统或者哪个域名生成的。
    场景:比如你之前的配置 Issuer: blog-service。当多租户或微服务网关收到 Token 时,可以通过 iss 判定这个 Token 是不是自家的、或者是哪个第三方认证中心(如 Google、微信)发过来的,从而决定用哪个公钥去验签。
  2. Subject (sub) — 主题
    作用:通常用来存放用户唯一标识(如 User ID)。
    场景:在面向用户的系统里,通常会把用户在数据库的自增 ID(如 “10025”)或 UUID 存到 sub 里。当后端通过验证后,直接读取 sub 就能知道当前操作的合法用户是谁。
  3. Audience (aud) — 受众
    作用:指定哪些客户端、业务系统或 API 有权接收并处理这个 Token。
    场景:假设你的架构里有“商城系统”和“博客系统”。你登录商城时拿到一个 Token,商城系统签发时设置 aud: “shop-app”。如果你企图拿这个 Token 去请求博客系统,博客系统校验时发现 aud 不是 “blog-app”,就会拒绝访问,防止 Token 被滥用(串门)。
  4. ExpiresAt (exp) — 过期时间
    作用:一个特定的 Unix 时间戳,表示在这个时间之后,Token 彻底失效。
    场景:非常核心的安全字段。JWT 具有状态不可控的特性,一旦发出无法轻易撤回。通过设置 exp(比如当前时间 + 2小时),可以保证即便 Token 泄露,黑客也只能在有限时间内利用它。
  5. NotBefore (nbf) — 生效时间
    作用:一个特定的 Unix 时间戳,定义了这个 Token 从什么时间点开始才能被业务系统接受。
    场景:比较少用。通常用于“预约/定时激活”的业务。例如系统进行升级,或者给用户发放一个 10 分钟后才生效的临时凭证,在 nbf 时间之前,即使签名正确、没过期,服务器也会拒绝。
  6. IssuedAt (iat) — 签发时间
    作用:一个特定的 Unix 时间戳,记录了这个 Token 被创建出来的时刻。
    场景:用来算距离过期还有多久。用于安全审计或密码重置后的 Token 批量失效:比如用户在 12:00 重置了密码,服务器可以设定“所有 iat 早于 12:00 的老 Token 全部拒收”。
  7. ID (jti) — Token 唯一标识
    作用:给这个具体的 Token 分配一个全局唯一的 ID(通常是 UUID)。
    场景:主要为了防重放攻击(Replay Attacks)。如果你的接口非常敏感,可以把通过验证的 jti 扔进 Redis 里存 2 小时(跟过期时间一致)。如果黑客拦截了请求想反复提交,服务器查到这个 jti 已经被用过一次了,直接拦截。


评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码