JSON Web Token 介绍以及使用 | 青训营

154 阅读4分钟

JSON Web Token (简称JWT),是一种用于身份验证和授权的开放标准

它由JSON(JavaScript Object Notation)格式组成,使用数字签名或加密方式生成令牌。JWT令牌可以在客户端和服务器之间传输,并用于验证用户的身份和授权访问资源。

JWT通常由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

头部包含了令牌的类型以及所使用的签名算法。例如,常见的签名算法包括HMAC SHA256和RSA。载荷包含了相关的声明和数据,如用户ID、角色、过期时间等。签名部分由头部、载荷和密钥组成,用于验证令牌的真实性和完整性。

JWT的工作流程如下:当用户在客户端进行身份认证后,服务器会生成一个JWT令牌并将其返回给客户端。客户端在后续的请求中携带该令牌作为身份验证凭证。服务器在接收到请求后,会使用相同的密钥对令牌进行验证,并根据令牌中的声明判断用户的身份和权限。

JWT的优点

优点1是无状态性,即服务器不需要存储任何会话信息。这是因为JWT令牌中已经包含了用户的相关信息,服务器只需要校验令牌的真实性即可。这种无状态的设计使得JWT适用于分布式和微服务架构,减轻了服务器的负担。

优点2是JWT的跨域支持。由于JWT令牌是在 HTTP 头部中进行传递的,因此可以轻松地在不同域之间进行传输。这使得在分布式系统中,不同服务之间的身份验证和授权变得更加容易。

下列场景中使用JSON Web Token是很有用的:

  • Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
  • Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

为什么需要JWT

与传统网站web项目使用Cookie-Session模式认证用户不同,JWT是一种基于Token的轻量级认证模式,服务端认证通过后,会生成一个JSON对象,用户得到Token令牌后,在后续直接使用令牌即可通过服务器访问用户的信息了。

使用JWT

我们使用 Go 语言社区中的 jwt 相关库来构建我们的应用,例如:github.com/golang-jwt/…

创建jwt

使用默认声明创建jwt

// 用于签名的字符串 var SigningKey = []byte("example.com")

// GenRegisteredClaims 使用默认声明创建jwt

func GenRegisteredClaims() (string, error) {

claims := &jwt.RegisteredClaims{

ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 8)), // 过期时间

Issuer: "user", // 签发人

}

// 生成token对象

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

// 生成签名字符串

return token.SignedString(SigningKey)

}

// 解析jwt

func ValidateRegisteredClaims(tokenString string) bool {

// 解析token

token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{},

error) {

return SigningKey, nil

})

if err != nil { // 解析token失败

return false

}

return token.Valid

}

生成JWT

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

// 创建一个我们自己的声明

claims := CustomClaims{

username, // 自定义字段

jwt.RegisteredClaims{

ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),

Issuer: "my-project", // 签发人

},

}

// 使用指定的签名方法创建签名对象

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

// 使用指定的secret签名并获得完整的编码后的字符串token

return token.SignedString(CustomSecret)

}

解析JWT

// ParseToken 解析JWT

func ParseToken(tokenString string) (*CustomClaims, error) {

// 解析token

// 如果是自定义Claim结构体则需要使用 ParseWithClaims 方法

token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, err error) {

// 直接使用标准的Claim则可以直接使用Parse方法

// token, err := jwt.Parse(tokenString, func(token jwt.Token) (i interface{}, err error){

return CustomSecret, nil

})

if err != nil {

return nil, err

}

// 对token对象中的Claim进行类型断言

if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { // 校验token

return claims, nil

}

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