Golang中如何处理几乎所有代码都需要的用户模块
Golang中如何处理几乎所有代码都需要的用户模块 在Go Web应用中,你如何传递已认证的用户结构体?
认证发生在中间件中,而几乎所有处理请求的代码都需要用户信息。
我可以将其存储在Context中,但某种程度上我不喜欢这种方式,因为这种存储不是静态类型的。
或者我几乎给每个方法都添加一个参数。
你是如何处理这个问题的?
3 回复
一种实现方式是在一个早期的处理程序中,将已认证的用户信息存入请求的上下文(Context)中,然后在每个处理程序中再将其取出。
func main() {
fmt.Println("hello world")
}
更多关于Golang中如何处理几乎所有代码都需要的用户模块的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go Web应用中处理用户认证信息传递,我通常采用类型安全的context方式。以下是我的实现方案:
package main
import (
"context"
"net/http"
)
// 定义context key类型,避免字符串冲突
type contextKey string
const userContextKey contextKey = "user"
// 用户结构体
type User struct {
ID int
Username string
Email string
Role string
}
// 中间件:认证并设置用户到context
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 从请求中获取认证信息(如JWT、Session等)
token := r.Header.Get("Authorization")
// 2. 验证token并获取用户信息
user, err := authenticate(token)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// 3. 创建新的context并设置用户
ctx := context.WithValue(r.Context(), userContextKey, user)
// 4. 使用新的context继续处理
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// 类型安全的context获取函数
func UserFromContext(ctx context.Context) (*User, bool) {
user, ok := ctx.Value(userContextKey).(*User)
return user, ok
}
// 必须获取用户的版本(失败时panic)
func MustUserFromContext(ctx context.Context) *User {
user, ok := UserFromContext(ctx)
if !ok {
panic("user not found in context")
}
return user
}
// 处理器中使用用户信息
func userProfileHandler(w http.ResponseWriter, r *http.Request) {
// 安全获取用户
if user, ok := UserFromContext(r.Context()); ok {
// 使用user处理业务逻辑
fmt.Fprintf(w, "Welcome %s!", user.Username)
return
}
http.Error(w, "Unauthorized", http.StatusUnauthorized)
}
// 需要强制存在用户的处理器
func adminHandler(w http.ResponseWriter, r *http.Request) {
user := MustUserFromContext(r.Context())
if user.Role != "admin" {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
// 管理员操作
fmt.Fprintf(w, "Admin area: %s", user.Username)
}
// 业务逻辑函数示例
func processOrder(ctx context.Context, orderID string) error {
user, ok := UserFromContext(ctx)
if !ok {
return errors.New("user not authenticated")
}
// 使用user.ID进行数据库操作
fmt.Printf("Processing order %s for user %d\n", orderID, user.ID)
return nil
}
// 在HTTP处理器中调用业务函数
func orderHandler(w http.ResponseWriter, r *http.Request) {
// 传递context给业务层
err := processOrder(r.Context(), "order123")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/profile", userProfileHandler)
mux.HandleFunc("/admin", adminHandler)
mux.HandleFunc("/order", orderHandler)
// 应用认证中间件
handler := AuthMiddleware(mux)
http.ListenAndServe(":8080", handler)
}
// 模拟认证函数
func authenticate(token string) (*User, error) {
if token == "" {
return nil, errors.New("no token provided")
}
// 实际项目中这里会有JWT解析或数据库查询
return &User{
ID: 1,
Username: "john_doe",
Email: "john@example.com",
Role: "user",
}, nil
}
对于需要深度传递的场景,可以结合依赖注入:
// 服务层结构体携带用户上下文
type OrderService struct {
user *User
}
func NewOrderService(ctx context.Context) (*OrderService, error) {
user, ok := UserFromContext(ctx)
if !ok {
return nil, errors.New("user required")
}
return &OrderService{user: user}, nil
}
func (s *OrderService) CreateOrder(productID string) error {
// 直接使用s.user,无需重复传递
fmt.Printf("Creating order for user %s\n", s.user.Username)
return nil
}
// 使用示例
func createOrderHandler(w http.ResponseWriter, r *http.Request) {
service, err := NewOrderService(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
err = service.CreateOrder("prod_123")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
}
这种方案保持了类型安全,避免了每个函数都添加用户参数,同时利用Go的context标准机制,与HTTP请求生命周期完美集成。


