前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go使用JWT完成认证

Go使用JWT完成认证

原创
作者头像
孟斯特
发布2023-12-08 19:35:14
4944
发布2023-12-08 19:35:14
举报
文章被收录于专栏:code人生code人生

Token 简介

在应用开发中,使用令牌(Token)是一种常见的身份验证和授权机制。以下是一些使用令牌的主要原因:

  1. 安全性: 令牌是一种安全的身份验证方式。相比于传统的用户名和密码验证方式,令牌可以更好地保护用户的凭证信息。通过使用令牌,应用可以在不传递用户凭证的情况下完成身份验证。
  2. 无状态性: 令牌机制使得服务器可以在不保存用户状态的情况下完成身份验证。每个请求都携带了足够的信息(令牌)来进行身份验证和授权,而不需要在服务器端保存大量的用户信息。
  3. 跨平台和跨服务: 由于令牌是一种标准化的身份验证机制,它可以被用于跨平台和跨服务的身份验证。一个令牌可以在多个服务之间传递,而不需要每个服务都保存用户凭证。
  4. 授权: 令牌不仅可以用于身份验证,还可以包含有关用户的授权信息。通过在令牌中添加一些声明(claims),可以实现细粒度的授权,确保用户只能访问其有权限的资源。
  5. 易于集成: 多数开发框架和第三方服务都提供了对令牌的支持。这使得开发者可以方便地将令牌集成到他们的应用中,而无需从头开始实现身份验证系统。
  6. 可调整的过期时间: 令牌通常具有过期时间,这使得安全性得到提高。即使令牌被截获,由于其过期,攻击者也只能在有限的时间内使用。
  7. 减轻密码管理: 对于移动应用或第三方应用,令牌可以用于避免存储用户的敏感信息(如密码)。用户只需提供一次凭证,然后获得一个令牌,之后的请求都使用令牌进行身份验证。

JWT 介绍

JSON Web Token(JWT)是一种用于在网络上安全传输声明的一种开放标准(RFC 7519)。它由一串经过 Base64 编码的 JSON 对象组成,可以包含用户的一些身份信息,以便在不同系统之间安全传输。

JWT 主要由三个部分组成:

  1. Header(头部): 头部通常由两部分组成,alg 表示签名算法(HMAC SHA256、RSA等),typ 表示令牌类型,这两部分会被 Base64 编码。
  2. Payload(载荷): 载荷包含了一些声明(claims)。声明是关于实体(通常是用户)和其他数据的声明。有三种类型的声明:
    • 注册声明(Registered claims): 这些声明是预定义的,不是强制要求的,但被推荐使用。例如,iss 表示令牌的发行者,sub 表示令牌的主题,exp 表示令牌的过期时间等。
    • 公共声明(Public claims): 这些声明被定义为在 JWT 中定义的标准化名称,但可以根据需要定义新的声明。
    • 私有声明(Private claims): 这些是自定义声明,供应用程序使用,不会与 JWT 的标准冲突。
  3. Signature(签名): 签名部分由编码后的头部、编码后的载荷以及一个秘钥共同组成,用于验证消息的完整性。签名的创建过程:
    • 将编码后的头部和编码后的载荷用点号连接起来,形成未加密的 JWT。
    • 使用指定的算法(如 HMAC SHA256)和秘钥对未加密的 JWT 进行签名。

JWT 的主要用途是在用户和服务器之间传递安全的身份信息。由于其轻量且易于使用,它已成为许多身份验证和授权协议的标准。

由于 JWT 的载荷(Payload)信息是 Base64 编码的,所以不应该在 JWT 中放置敏感信息,例如密码等。

实现示例

对接第三方 API 通常涉及到以下几个步骤:获取访问令牌(token)、使用令牌进行 API 请求、处理 API 响应,以及在需要时刷新令牌。下面是一个简单的示例,演示如何使用github.com/golang-jwt/jwt/v5库在 Go 中实现请求token、刷新token以及封装请求:

代码语言:go
复制
package main

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/golang-jwt/jwt/v5"
	"github.com/pkg/errors"
)

// SecretKey 用于签名和验证的密钥
var SecretKey = []byte("your-secret-key")
var expire = 30 * time.Minute

// GenerateToken 生成 JWT
func GenerateToken() (string, error) {
	claims := &jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(expire)),
		Issuer:    "example",
		Subject:   "example",
	}

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

	signedToken, err := token.SignedString(SecretKey)
	if err != nil {
		return "", err
	}

	return signedToken, nil
}

// RefreshToken 刷新 JWT
func RefreshToken(tokenString string) (string, error) {

	// 解析 token
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return SecretKey, nil
	})

	if err != nil || !token.Valid {
		return "", fmt.Errorf("invalid token")
	}

	// 验证 token 是否有效
	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return "", fmt.Errorf("invalid claims")
	}
	exp, err := claims.GetExpirationTime()
	if err != nil {
		return "", errors.Wrap(err, "GetExpirationTime from token error")
	}

	if time.Until(exp.Time) < 0 {
		return "", errors.New("the token expires")
	}

	return GenerateToken()
}

// AuthMiddleware 中间件,用于验证请求中的 token
func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tokenString := r.Header.Get("Authorization")

		if tokenString == "" {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}

		// 解析 token
		token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			return SecretKey, nil
		})

		if err != nil || !token.Valid {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}

		// 将解析后的用户信息存储在上下文中,以便后续处理函数使用
		claims, ok := token.Claims.(jwt.MapClaims)
		if !ok {
			http.Error(w, "Unauthorized", http.StatusUnauthorized)
			return
		}
		ctx := context.WithValue(r.Context(), "user", claims["sub"])
		r = r.WithContext(ctx)

		next.ServeHTTP(w, r)
	})
}

// ExampleHandler 示例处理程序,需要通过 AuthMiddleware 进行身份验证
func ExampleHandler(w http.ResponseWriter, r *http.Request) {
	user := r.Context().Value("user")
	w.Write([]byte(fmt.Sprintf("Hello, %s!", user)))
}

func main() {
	// 示例代码中使用的路由是伪代码,请根据你的实际项目使用适当的路由设置
	mux := http.NewServeMux()

	// 处理 /login 路径,生成 token
	mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		token, err := GenerateToken()
		if err != nil {
			http.Error(w, "Failed to generate token", http.StatusInternalServerError)
			return
		}
		w.Write([]byte(token))
	})

	// 处理 /refresh 路径,刷新 token
	mux.HandleFunc("/refresh", func(w http.ResponseWriter, r *http.Request) {
		tokenString := r.Header.Get("Authorization")
		newToken, err := RefreshToken(tokenString)
		if err != nil {
			http.Error(w, "Failed to refresh token", http.StatusInternalServerError)
			return
		}
		w.Write([]byte(newToken))
	})

	// 处理其他路径,需要通过 AuthMiddleware 进行身份验证
	mux.Handle("/example", AuthMiddleware(http.HandlerFunc(ExampleHandler)))

	fmt.Println("service start")
	http.ListenAndServe(":8080", mux)
}

我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!


声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Token 简介
  • JWT 介绍
  • 实现示例
相关产品与服务
多因子身份认证
多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com