Go使用JWT构建用户认证系统


当前第2页 返回上一页

为了处理这个特殊的场景,我们将这个customkey值添加到RefreshToken的有效负载中。在验证RefreshToken期间,我们将再次使用用户ID和HashToken创建这个customKey,并将其与RefreshToken有效负载中的customKey进行比较。所以现在如果这个HashToken改变了,那么RefreshToken就会失效。每次用户更改密码时,我们都会用一个新的随机字符串来更改这个HashToken。因此,我们为用户提供了一种通过修改密码来使RequestToken无效的方法。希望我对引入HashToken的原因进行冗长的解释是有意义的,我们将继续讨论下一部分,即创建JWT有效负载。

RefreshTokenCustomClaims构成了我们的有效负载。它由诸如用户id、customkey、tokenttype、颁发者(属于JWT标准声明)等信息组成。注意,我们没有向有效负载添加任何过期时间戳信息。现在我们有了有效负载,头文件将由JWT库生成,接下来我们需要的是签名。我们这里使用的签名方法是RS256。

简而言之,RS256方法遵循公钥密码学,其中我们有两个密钥,公钥和私钥。私钥将用于对令牌进行签名,公钥将用于验证令牌,这与HSA方法不同,在HSA方法中,一个密钥将用于签名和验证。用私钥加密的数据只能用相应的公钥解密,反之亦然。

(注意:在解释JWT时,我们使用HSA方法进行签名。不同的是HSA是基于哈希的方法,RSA是基于加密的方法。为了进一步了解每种方法的优缺点,请阅读下面的参考资料。
linux系统中生成rsa密钥的命令:

  • 生成rsa私钥
openssl genrsa -out auth-private.pem 2048
  • 导出rsa公钥
openssl rsa -in auth-private.pem -outform PEM -pubout -out auth-public.pem

因此,我们从服务器文件系统读取privatekey。现在我们有了创建token所需的所有组件——有效负载、头(指定的签名方法)和签名秘密。我们只需要创建我们的令牌并使用秘密对其进行签名。是的! !我们有了刷新token。

接下来,我们使用类似的方法来创建AccessToken,只需很少的修改,比如使用不同的私钥(不包括customKey的需要)。这里需要注意的重要一点是,我们正在为Accesstoken添加一个过期时间戳。

// GenerateAccessToken generates a new access token for the given user
func (auth *AuthService) GenerateAccessToken(user *data.User) (string, error) {

    userID := user.ID
    tokenType := "access"

    claims := AccessTokenCustomClaims{
        userID,
        tokenType,
        jwt.StandardClaims{
            ExpiresAt: time.Now().Add(time.Minute * time.Duration(auth.configs.JwtExpiration)).Unix(),
            Issuer:    "bookite.auth.service",
        },
    }

    signBytes, err := ioutil.ReadFile(auth.configs.AccessTokenPrivateKeyPath)
    if err != nil {
        auth.logger.Error("unable to read private key", "error", err)
        return "", errors.New("could not generate access token. please try again later")
    }

    signKey, err := jwt.ParseRSAPrivateKeyFromPEM(signBytes)
    if err != nil {
        auth.logger.Error("unable to parse private key", "error", err)
        return "", errors.New("could not generate access token. please try again later")
    }

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

    return token.SignedString(signKey)
}

至此,我们已经介绍了与注册和登录处理函数有关的所有内容。

现在让我们看看如何处理刷新令牌请求。如前所述,我们使用refresh-token在AccessToken过期时获得一个新的AccessToken。因此,我们需要在这里验证给定的RefreshToken并提供一个新的AccessToken。验证部分在在refresh-token子外部注册的MiddlewareValidateRefreshToken中间件函数中执行。

// MiddlewareValidateRefreshToken validates whether the request contains a bearer token
// it also decodes and authenticates the given token
func (uh *UserHandler) MiddlewareValidateRefreshToken(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        uh.logger.Debug("validating refresh token")
        uh.logger.Debug("auth header", r.Header.Get("Authorization"))
        token, err := extractToken(r)
        if err != nil {
            uh.logger.Error("token not provided or malformed")
            w.WriteHeader(http.StatusBadRequest)
            data.ToJSON(&GenericError{Error: err.Error()}, w)
            return
        }
        uh.logger.Debug("token present in header", token)

        userID, customKey, err := uh.authService.ValidateRefreshToken(token)
        if err != nil {
            uh.logger.Error("token validation failed", "error", err)
            w.WriteHeader(http.StatusBadRequest)
            data.ToJSON(&GenericError{Error: err.Error()}, w)
            return
        }
        uh.logger.Debug("refresh token validated")

        user, err := uh.repo.GetUserByID(context.Background(), userID)
        if err != nil {
            uh.logger.Error("invalid token: wrong userID while parsing", err)
            w.WriteHeader(http.StatusBadRequest)
            data.ToJSON(&GenericError{Error: "invalid token: authentication failed"}, w)
            return
        }

        actualCustomKey := uh.authService.GenerateCustomKey(user.ID, user.TokenHash)
        if customKey != actualCustomKey {
            uh.logger.Debug("wrong token: authetincation failed")
            w.WriteHeader(http.StatusBadRequest)
            data.ToJSON(&GenericError{Error: "invalid token: authentication failed"}, w)
            return
        }

        ctx := context.WithValue(r.Context(), UserKey{}, *user)
        r = r.WithContext(ctx)

        next.ServeHTTP(w, r)
    })
}
func extractToken(r *http.Request) (string, error) {
    authHeader := r.Header.Get("Authorization")
    authHeaderContent := strings.Split(authHeader, " ")
    if len(authHeaderContent) != 2 {
        return "", errors.New("Token not provided or malformed")
    }
    return authHeaderContent[1], nil
}

本文来自:简书

感谢作者:汪明军_3145

查看原文:Go使用JWT构建用户认证系统

返回前面的内容

相关阅读 >>

一道有趣的Golang排错题

Golang检查文件是否存在的方法

手撸Golang 基本数据结构与算法 归并排序

Golang可以写单片机吗

Golang map无法删除元素吗

Go属于解释型语言么

Golang web开发乱码的原因与解决方法

Golang怎么判断字符串是否为空

手撸Golang 基本数据结构与算法 栈

kotlin coroutines vs Goroutines

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...