首页
Preview

在Golang中使用JWT——如何实现基于令牌的身份验证

设计者:canopas

我们在应用程序中使用两种类型的令牌进行身份验证:

  • 访问令牌(Access Token):用于在客户端上访问受保护的资源的字符串。每个令牌都具有特定的范围、生命周期和其他有效属性。我们可以根据需要设置生命周期(1小时或1天)。
  • 刷新令牌(Refresh Token):用于获取访问令牌的字符串。在授权时,它将在颁发访问令牌时创建。其生命周期应大于访问令牌(1个月或1年)。

请记住,这些令牌不仅适用于JWT。它们提供了一种授权资源的方法。我们可以使用JWT或任何其他格式创建这些令牌。

为什么需要刷新令牌?

在第一个访问令牌失效后,刷新令牌用于对用户进行身份验证。在不损害安全性的情况下,这一过程在后台进行,提高了用户体验。刷新令牌不会授予用户任何超出最初允许的访问权限。

如果你想了解更多信息,可以参考这个线程

赞助

安全大多是人类行为的问题,行为可以通过习惯来定义。通过justly来改善你的习惯吧!

Justly - Systems, Habits, Goals

目录

1. Introduction
2. Terminologies by a real-life example
3. Generate tokens
4. Validate tokens
5. Refresh access token using the refresh token
6. Conclusion

简介

JWT(JSON Web Token)是用于在任何应用程序中进行身份验证的令牌格式。jwt.io提供了一个Web界面,用于检查JWT令牌,以及一个用户指南,以了解令牌结构。

JWT令牌包含三个部分:

  • 标头(Header) -(包含令牌类型和令牌的签名算法)
  • 签名(Signature) -(用于验证消息沿途未被更改的base64密钥)
  • 有效载荷(Payload) -(包含实际数据,即用于创建令牌的声明(claims))

我们将使用go-jwt包来实现Golang JWT身份验证。

以真实生活例子为例的术语

我们可以将JWT术语简单地与住宅锁定系统联系起来,如下所示:

  • 刷新令牌:房门
  • 访问令牌:门锁
  • 标头:用于制作锁和钥匙的物品和材料
  • 密钥(签名):锁的钥匙
  • 范围:家庭和所有者
  • 声明:锁制造商提供的有效期(锁的寿命)
  • 生成令牌:所有者购买了一把锁
  • 验证令牌:只有使用匹配的钥匙才能打开锁。
  • 更新访问令牌:当锁被损坏时,所有者更换锁。门及其功能将被视为购买新锁。

生成令牌

要生成令牌,我们需要为两种令牌(或可以同时使用相同的密钥)分别定义一个单独的密钥。你可以使用HSA 256或RS256加密生成它。它至少应为32个字符长,但越长越好。

接下来,定义声明。go-jwt有其JWT的标准声明。但是我们可以创建自定义声明,如下所示:

type JWTData struct {
   jwt.StandardClaims
   CustomClaims map[string]string `json:"custom_claims"`
}

让我们使用这些声明生成令牌:

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

func GenerateJWTAccessToken(userId string, email string) (string, error) {
    // prepare claims for token
    claims := JWTData{
        StandardClaims: jwt.StandardClaims{
           // set token lifetime in timestamp
           ExpiresAt: time.Now().Add(time.Duration(tokenLifeTime)).Unix(),
        },
        // add custom claims like user_id or email, 
        // it can vary according to requirements
        CustomClaims: map[string]string{
           "user_id": userId,
           "email": email,
        },
     }
    
     // generate a string using claims and HS256 algorithm
     tokenString := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    
     // sign the generated key using secretKey
     token, err := tokenString.SignedString(secretKey)

     return token, err
}

token将是包含标头、签名和有效载荷的JWT令牌。你可以在jwt.io上检查令牌。

相同的方法可以用于生成具有更长寿命的刷新令牌。

验证令牌

要验证授权的资源,我们需要首先验证令牌。

下面的代码将用于验证令牌:

claims := &JWTData{}

 _, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
      return secretKey, nil
 })
  • ParseWithClaims的最后一个参数是KeyFunc,它将从给定的令牌返回secretKey,
func(token *jwt.Token) (interface{}, error) {
      return secretKey, nil
 }
  • parseWithClaims将数据分配给我们之前在JWTData中定义的claims

我们可以通过声明ExpiresAt来验证令牌。它还具有自定义声明user_idemail。你可以检查它们是否存在于你的数据库中,并使用给定的令牌授权用户。

以下是如果令牌无效的错误代码:

if err != nil {
  // check whether error is validation error or not
  if validationErr, ok := err.(*jwt.ValidationError); ok {
   // Token is malformed
   if validationErr.Errors & jwt.ValidationErrorMalformed != 0 {
    return "Token is malformed"
   } else if validationErr.Errors & (jwt.ValidationErrorExpired | jwt.ValidationErrorNotValidYet) != 0 {
    // Token is either expired or not active yet
    return "Token is either expired or not active yet"
   } else {
    return "Other err"
   }
  } else {
   return "Other err"
  }
 }

使用刷新令牌更新访问令牌

正如我们所知道的那样,刷新令牌将用于更新访问令牌。

以下是使用刷新令牌获取访问令牌的流程:

  • 使用上面的方法验证刷新令牌。

注意:你可以将刷新令牌传递给标题或表单数据中,具体取决于哪种更可取。

_, err := jwt.ParseWithClaims(refreshToken, claims, func(token *jwt.Token) (interface{}, error) {
      return secretKey, nil
 })
  • 使用自定义声明检查用户是否存在于数据库中。
  • 如果刷新令牌有效且用户存在,则使用相同范围定义的寿命生成新的访问令牌和刷新令牌。

考虑一种情况,其中刷新令牌的寿命为1个月,而用户已经2个月没有使用该应用程序。如果在给定时间内未使用刷新令牌,则刷新令牌将过期,此后需要重新请求新的刷新令牌和访问令牌。否则,它将刷新访问令牌。

结论

JWT身份验证使用签名的令牌在两个参与方之间提供了安全性。

它是用于身份验证的广泛使用格式。我已经解释了它在Golang中的使用,但是流程对于其他语言(如JavaScript或PHP)也是相同的。

译自:https://blog.canopas.com/jwt-in-golang-how-to-implement-token-based-authentication-298c89a26ffd

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
菜鸟一只
你就是个黄焖鸡,又黄又闷又垃圾。

评论(0)

添加评论