JWT(JSON Web Token)中间件包提供了一种简单而安全的方式来处理用户认证。这个包允许你轻松地在你的Go应用程序中集成JWT认证,包括令牌的生成、验证和解析。
以下是一个基本的使用示例:
package main
import (
"fmt"
"net/http"
"github.com/sagoo-cloud/nexframe/auth"
"github.com/gorilla/mux"
)
func main() {
// 创建JWT中间件实例
jwtMiddleware, err := auth.NewJwt()
if err != nil {
panic(err)
}
// 创建路由
r := mux.NewRouter()
// 应用JWT中间件到受保护的路由
r.Handle("/protected", jwtMiddleware.Middleware(http.HandlerFunc(protectedHandler))).Methods("GET")
// 启动服务器
http.ListenAndServe(":8080", r)
}
func protectedHandler(w http.ResponseWriter, r *http.Request) {
username, err := auth.GetCurrentUser(r.Context())
if err != nil {
http.Error(w, "未授权", http.StatusUnauthorized)
return
}
fmt.Fprintf(w, "欢迎, %s!", username)
}
JWT中间件使用 configs.TokenConfig
进行配置。你需要确保你的配置文件或环境变量中包含以下信息:
SigningKey
: 用于签名和验证令牌的密钥ExpiresTime
: 访问令牌的有效期RefreshExpiresTime
: 刷新令牌的有效期Issuer
: 令牌的签发者TokenLookup
: 指定从哪里提取令牌(例如:"header:Authorization")Method
: 签名方法(支持"HS256"、"HS384"、"HS512")
使用 NewJwt()
函数创建一个新的JWT中间件实例:
jwtMiddleware, err := auth.NewJwt()
if err != nil {
// 处理错误
}
使用 GenerateTokenPair()
方法生成一个包含访问令牌和刷新令牌的令牌对:
tokenPair, err := jwtMiddleware.GenerateTokenPair("username")
if err != nil {
// 处理错误
}
fmt.Printf("访问令牌: %s\n", tokenPair.AccessToken)
fmt.Printf("刷新令牌: %s\n", tokenPair.RefreshToken)
使用 Middleware()
方法来验证请求中的令牌:
r.Handle("/protected", jwtMiddleware.Middleware(http.HandlerFunc(protectedHandler))).Methods("GET")
使用 RefreshToken()
方法刷新访问令牌:
newTokenPair, err := jwtMiddleware.RefreshToken(oldRefreshToken)
if err != nil {
// 处理错误
}
在经过中间件处理的请求中,你可以使用 GetCurrentUser()
函数从上下文中获取用户信息:
username, err := auth.GetCurrentUser(r.Context())
if err != nil {
// 处理错误
}
你可以通过修改 JwtConfig
中的 ErrHandler
字段来自定义错误处理:
jwtMiddleware.conf.ErrHandler = func(w http.ResponseWriter, r *http.Request, err error) {
http.Error(w, "自定义错误: "+err.Error(), http.StatusUnauthorized)
}
默认情况下,中间件支持从请求头、查询参数和cookie中提取令牌。你可以通过修改 TokenLookup
配置来自定义提取方法:
config.TokenLookup = "header:X-API-Key" // 从自定义头部提取
- 始终使用HTTPS来传输令牌,避免中间人攻击。
- 设置合理的令牌过期时间,平衡安全性和用户体验。
- 定期轮换签名密钥。
- 在生产环境中使用足够长和复杂的密钥。
- 妥善保管刷新令牌,它们通常有更长的有效期。
以下是一个更完整的示例,展示了如何在一个简单的Web应用中使用JWT中间件:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
"github.com/sagoo-cloud/nexframe/auth"
)
var jwtMiddleware *auth.JwtMiddleware
func main() {
var err error
jwtMiddleware, err = auth.NewJwt()
if err != nil {
log.Fatalf("创建JWT中间件失败: %v", err)
}
r := mux.NewRouter()
r.HandleFunc("/login", loginHandler).Methods("POST")
r.HandleFunc("/refresh", refreshHandler).Methods("POST")
r.Handle("/protected", jwtMiddleware.Middleware(http.HandlerFunc(protectedHandler))).Methods("GET")
log.Println("服务器启动在 :8080")
log.Fatal(http.ListenAndServe(":8080", r))
}
func loginHandler(w http.ResponseWriter, r *http.Request) {
// 这里应该有实际的用户验证逻辑
username := r.FormValue("username")
password := r.FormValue("password")
if username == "admin" && password == "password" {
tokenPair, err := jwtMiddleware.GenerateTokenPair(username)
if err != nil {
http.Error(w, "生成令牌失败", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(tokenPair)
} else {
http.Error(w, "无效的凭证", http.StatusUnauthorized)
}
}
func refreshHandler(w http.ResponseWriter, r *http.Request) {
refreshToken := r.FormValue("refresh_token")
if refreshToken == "" {
http.Error(w, "缺少刷新令牌", http.StatusBadRequest)
return
}
newTokenPair, err := jwtMiddleware.RefreshToken(refreshToken)
if err != nil {
http.Error(w, "刷新令牌失败", http.StatusUnauthorized)
return
}
json.NewEncoder(w).Encode(newTokenPair)
}
func protectedHandler(w http.ResponseWriter, r *http.Request) {
username, err := auth.GetCurrentUser(r.Context())
if err != nil {
http.Error(w, "未授权", http.StatusUnauthorized)
return
}
fmt.Fprintf(w, "欢迎, %s!", username)
}
Q: 如何处理令牌过期?
A: 当访问令牌过期时,使用刷新令牌调用 RefreshToken()
方法获取新的令牌对。
Q: 可以在令牌中包含自定义信息吗?
A: 目前,令牌中只包含用户名和标准的JWT声明。如果需要包含更多信息,你需要修改 TokenClaims
结构体和相关的生成、解析逻辑。
Q: 如何在测试中模拟认证?
A: 你可以使用 GenerateTokenPair()
函数创建一个有效的令牌对,然后在测试请求的头部中包含访问令牌。
Q: 中间件是否线程安全? A: 是的,中间件的设计考虑了并发使用的情况,可以安全地在多个 goroutine 中使用。