1.每一行代码都有详细注释,解释了其功能和作用。这些注释可以帮助你理解代码如何工作,特别是在处理用户登录、生成 JWT、验证 JWT 和返回用户信息的过程中。
package main // 指定这个文件是一个可执行程序
import (
"fmt" // 标准库包,用于格式化字符串和输出到控制台
"github.com/dgrijalva/jwt-go" // 第三方库,用于处理 JWT
"github.com/gin-gonic/gin" // 第三方库,用于构建 Web API
"net/http" // 标准库包,提供 HTTP 客户端和服务器实现
"time" // 标准库包,提供时间的基本操作
)
var jwtKey = []byte("your_secret_key") // 用于签名 JWT 的密钥
// Claims 定义了包含用户信息的自定义 JWT 结构
type Claims struct {
Identity int `json:"identity"` // 用户 ID
jwt.StandardClaims // 域包含 JWT 标准的声明,如过期时间、发布者等
}
func main() {
r := gin.Default() // 创建一个默认的 Gin 路由器实例
r.POST("/login", login) // 定义 POST 方法的 /login 路由,处理用户登录请求
r.GET("/user", authenticate, getUserId) // 定义 GET 方法的 /user 路由,先进行用户验证再获取用户 ID
// 生成一个有效的 JWT token 并打印出来
generateAndPrintToken()
r.Run(":8080") // 启动服务器,监听端口 8080
}
// login 处理用户登录请求,生成 JWT token 并返回给客户端
func login(c *gin.Context) {
identity := 135 // 模拟用户的 ID
// 创建包含用户 ID 和过期时间的 JWT claims
expirationTime := time.Now().Add(5 * time.Minute) // 设置 token 的过期时间为当前时间的 5 分钟后
claims := &Claims{
Identity: identity, // 设置用户的 ID
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(), // 设置 JWT 的过期时间
},
}
// 创建 JWT token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // 使用 HS256 签名方法创建 JWT
tokenString, err := token.SignedString(jwtKey) // 使用密钥签名生成 JWT
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法创建token"}) // 如果签名失败,返回 500 错误
return
}
// 返回 token 给客户端
c.JSON(http.StatusOK, gin.H{"token": tokenString})
}
// authenticate 中间件,验证请求是否携带有效的 JWT token
func authenticate(c *gin.Context) {
tokenString := c.GetHeader("Authorization") // 从请求头中获取 Authorization 字段的值
// 解析 token
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil // 返回用于签名的密钥
})
if err != nil || !token.Valid { // 如果解析或验证失败
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "无效的token"}) // 中止请求并返回 401 错误
return
}
// 将 claims 保存到上下文中,以便后续处理使用
c.Set("claims", claims)
c.Next() // 继续处理下一个处理函数
}
// getUserId 从上下文中提取用户 ID 并返回给客户端
func getUserId(c *gin.Context) {
data := ExtractClaims(c) // 从上下文中提取 claims
if data == nil {
fmt.Println(fmt.Sprintf("%s [WARNING] %s %s GetUserId 缺少 identity", time.Now().Format(time.RFC3339), c.Request.Method, c.Request.URL.Path))
c.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户ID"}) // 如果没有提取到 claims,返回 401 错误
return
}
//identity := data.Identity // 获取用户 ID
//c.JSON(http.StatusOK, gin.H{"userId": identity}) // 返回用户 ID 给客户端
c.JSON(http.StatusOK, gin.H{"data": data}) // 返回用户 ID 给客户端
}
// ExtractClaims 从上下文中提取 JWT claims
func ExtractClaims(c *gin.Context) *Claims {
claims, exists := c.Get("claims") // 从上下文中获取 claims
if !exists {
return nil // 如果 claims 不存在,返回 nil
}
return claims.(*Claims) // 返回 claims
}
// generateAndPrintToken 生成一个新的 JWT token 并打印到控制台
func generateAndPrintToken() {
identity := 135 // 模拟用户的 ID
// 创建包含用户 ID 和过期时间的 JWT claims
expirationTime := time.Now().Add(5 * time.Minute) // 设置 token 的过期时间为当前时间的 5 分钟后
claims := &Claims{
Identity: identity, // 设置用户的 ID
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(), // 设置 JWT 的过期时间
},
}
// 创建 JWT token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // 使用 HS256 签名方法创建 JWT
tokenString, err := token.SignedString(jwtKey) // 使用密钥签名生成 JWT
if err != nil {
fmt.Println("无法创建token:", err) // 如果签名失败,打印错误
return
}
fmt.Println("生成的token:", tokenString) // 打印生成的 token
}
2.好的,我们可以将代码分为两个部分:一个处理用户登录,并生成 JWT token;另一个验证 JWT token 并返回用户信息。以下是详细分类和注释:
(1)用户登录部分
package main // 指定这个文件是一个可执行程序
import (
"fmt" // 标准库包,用于格式化字符串和输出到控制台
"net/http" // 标准库包,提供 HTTP 客户端和服务器实现
"time" // 标准库包,提供时间的基本操作
"github.com/dgrijalva/jwt-go" // 第三方库,用于处理 JWT
"github.com/gin-gonic/gin" // 第三方库,用于构建 Web API
)
var jwtKey = []byte("your_secret_key") // 用于签名 JWT 的密钥
// Claims 定义了包含用户信息的自定义 JWT 结构
type Claims struct {
Identity int `json:"identity"` // 用户 ID
jwt.StandardClaims // 域包含 JWT 标准的声明,如过期时间、发布者等
}
func main() {
r := gin.Default() // 创建一个默认的 Gin 路由器实例
r.POST("/login", login) // 定义 POST 方法的 /login 路由,处理用户登录请求
r.GET("/user", authenticate, getUserId) // 定义 GET 方法的 /user 路由,先进行用户验证再获取用户 ID
// 生成一个有效的 JWT token 并打印出来
generateAndPrintToken()
r.Run(":8080") // 启动服务器,监听端口 8080
}
// 用户登录部分:处理用户登录请求,生成 JWT token 并返回给客户端
func login(c *gin.Context) {
identity := 135 // 模拟用户的 ID
// 创建包含用户 ID 和过期时间的 JWT claims
expirationTime := time.Now().Add(5 * time.Minute) // 设置 token 的过期时间为当前时间的 5 分钟后
claims := &Claims{
Identity: identity, // 设置用户的 ID
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(), // 设置 JWT 的过期时间
},
}
// 创建 JWT token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // 使用 HS256 签名方法创建 JWT
tokenString, err := token.SignedString(jwtKey) // 使用密钥签名生成 JWT
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法创建token"}) // 如果签名失败,返回 500 错误
return
}
// 返回 token 给客户端
c.JSON(http.StatusOK, gin.H{"token": tokenString})
}
// generateAndPrintToken 生成一个新的 JWT token 并打印到控制台
// 仅用于辅助测试,在实际应用中可能不会使用
func generateAndPrintToken() {
identity := 135 // 模拟用户的 ID
// 创建包含用户 ID 和过期时间的 JWT claims
expirationTime := time.Now().Add(5 * time.Minute) // 设置 token 的过期时间为当前时间的 5 分钟后
claims := &Claims{
Identity: identity, // 设置用户的 ID
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(), // 设置 JWT 的过期时间
},
}
// 创建 JWT token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) // 使用 HS256 签名方法创建 JWT
tokenString, err := token.SignedString(jwtKey) // 使用密钥签名生成 JWT
if err != nil {
fmt.Println("无法创建token:", err) // 如果签名失败,打印错误
return
}
fmt.Println("生成的token:", tokenString) // 打印生成的 token
}
(2)返回用户信息部分
// authenticate 中间件,验证请求是否携带有效的 JWT token
// 适用于返回用户信息的部分
func authenticate(c *gin.Context) {
tokenString := c.GetHeader("Authorization") // 从请求头中获取 Authorization 字段的值
// 解析 token
claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil // 返回用于签名的密钥
})
if err != nil || !token.Valid { // 如果解析或验证失败
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "无效的token"}) // 中止请求并返回 401 错误
return
}
// 将 claims 保存到上下文中,以便后续处理使用
c.Set("claims", claims)
c.Next() // 继续处理下一个处理函数
}
// getUserId 从上下文中提取用户 ID 并返回给客户端
// 适用于返回用户信息的部分
func getUserId(c *gin.Context) {
data := ExtractClaims(c) // 从上下文中提取 claims
if data == nil {
fmt.Println(fmt.Sprintf("%s [WARNING] %s %s GetUserId 缺少 identity", time.Now().Format(time.RFC3339), c.Request.Method, c.Request.URL.Path))
c.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户ID"}) // 如果没有提取到 claims,返回 401 错误
return
}
identity := data.Identity // 获取用户 ID
c.JSON(http.StatusOK, gin.H{"userId": identity}) // 返回用户 ID 给客户端
}
// ExtractClaims 从上下文中提取 JWT claims
// 适用于返回用户信息的部分
func ExtractClaims(c *gin.Context) *Claims {
claims, exists := c.Get("claims") // 从上下文中获取 claims
if !exists {
return nil // 如果 claims 不存在,返回 nil
}
return claims.(*Claims) // 返回 claims
}
3.测试:
获取token:
使用生成的 JWT token 测试
在 Postman 中,按照以下步骤:
- 创建一个新的
GET
请求。 - 设置 URL 为
http://localhost:8080/user
。 - 在请求头中添加一个新的头字段
Authorization
,值为你在上面生成的 JWT token。 - 点击 "Send" 按钮。
你将会收到包含用户 ID 的 JSON 响应。