Golang实现用户认证与数据存储的最佳实践
Golang实现用户认证与数据存储的最佳实践 大家好,
我是大学三年级学生,这学期一直在做一个用Go编写的小型GRPC应用程序作为独立研究项目。它本质上是一个小型世界模拟,用户可以通过API调用与之互动,并通过GRPC流实时观看。大部分功能已经完成,现在需要攻克的下一个难题是用户身份验证。
目前我需要为大多数API调用(除了观看调用)实现身份验证,以及合适的存储方案(支持最高50MB的文件,每个用户可存储多个文件)。大家对实现身份验证和/或存储有什么建议吗?
以下是我的想法…
我可以尝试使用Firebase…我在Web应用中大量使用过它,它在身份验证和存储方面表现很好,但对于后端重度服务似乎不是最佳方案。我可以让用户在前端通过登录Firebase进行身份验证,但不确定如何将这种验证和令牌传递到服务器。我猜可以在GRPC调用的元数据中发送用户名和密码,然后再次访问Firebase服务器进行验证,但这感觉很不妥当。也可以让他们直接登录并通过我的服务创建令牌,但这样使用Firebase就节省不了多少时间。
另一个考虑是搭建Cassandra数据库,不过听说成本较高。我的项目已经准备好通过kubernetes部署,所以搭建数据库不会太困难,但我担心存储之前提到的较大文件的问题。同时我也担心实现身份验证会耗费大量时间。我一直在研究Auth0,但无法评估其实现难度。
目前我有点迷茫,非常希望能得到建议!我的指导老师在项目规划方面帮助有限 😄
更多关于Golang实现用户认证与数据存储的最佳实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html
太完美了!我之前完全不知道这个功能的存在,非常感谢。
更多关于Golang实现用户认证与数据存储的最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是什么类型的 API?REST?RPC?AMQP 消息?
后端部署在哪里?是否有可用的文件系统?
问题实在太多了……
你好。你可以使用 JWT 令牌 https://firebase.google.com/docs/auth/admin/verify-id-tokens
RPC客户端使用React构建,并通过npm包web-grpc调用后端服务。
目前我正通过Kubernetes部署,在Minikube环境中运行,后续可能迁移至GKE,但尚未完成文件系统配置。我倾向于选择Cassandra数据库作为存储方案。
对于信息不完整的问题深表歉意!
在Go中实现用户认证和数据存储,可以采用以下方案:
用户认证
推荐使用JWT(JSON Web Tokens)结合gRPC拦截器实现认证。以下是一个基础示例:
package main
import (
"context"
"fmt"
"strings"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"github.com/golang-jwt/jwt/v4"
)
type AuthInterceptor struct {
jwtSecret []byte
}
func (ai *AuthInterceptor) UnaryInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
// 跳过观看调用的认证
if strings.Contains(info.FullMethod, "Watch") {
return handler(ctx, req)
}
// 从元数据获取令牌
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, "missing metadata")
}
authHeaders := md["authorization"]
if len(authHeaders) == 0 {
return nil, status.Error(codes.Unauthenticated, "missing authorization header")
}
tokenString := strings.TrimPrefix(authHeaders[0], "Bearer ")
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return ai.jwtSecret, nil
})
if err != nil || !token.Valid {
return nil, status.Error(codes.Unauthenticated, "invalid token")
}
return handler(ctx, req)
}
}
// 登录处理示例
func Login(username, password string) (string, error) {
// 验证用户凭据(这里简化处理)
if username != "testuser" || password != "testpass" {
return "", status.Error(codes.Unauthenticated, "invalid credentials")
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"username": username,
"exp": jwt.TimeFunc().Add(24 * time.Hour).Unix(),
})
return token.SignedString([]byte("your-secret-key"))
}
数据存储
对于50MB以下的文件存储,建议使用本地文件系统或云存储服务。以下是本地存储的示例:
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
)
type FileStorage struct {
basePath string
}
func NewFileStorage(basePath string) *FileStorage {
return &FileStorage{basePath: basePath}
}
func (fs *FileStorage) SaveFile(userID string, fileData []byte, filename string) (string, error) {
// 创建用户目录
userDir := filepath.Join(fs.basePath, userID)
if err := os.MkdirAll(userDir, 0755); err != nil {
return "", fmt.Errorf("failed to create user directory: %v", err)
}
// 生成文件哈希作为唯一标识
hash := sha256.Sum256(fileData)
fileHash := hex.EncodeToString(hash[:])
// 保存文件
filePath := filepath.Join(userDir, fileHash+"_"+filename)
if err := os.WriteFile(filePath, fileData, 0644); err != nil {
return "", fmt.Errorf("failed to save file: %v", err)
}
return fileHash, nil
}
func (fs *FileStorage) GetFile(userID, fileHash string) ([]byte, error) {
userDir := filepath.Join(fs.basePath, userID)
// 查找匹配的文件
files, err := filepath.Glob(filepath.Join(userDir, fileHash+"_*"))
if err != nil {
return nil, fmt.Errorf("failed to search files: %v", err)
}
if len(files) == 0 {
return nil, fmt.Errorf("file not found")
}
return os.ReadFile(files[0])
}
// 使用示例
func main() {
storage := NewFileStorage("./user_files")
// 保存文件
fileID, err := storage.SaveFile("user123", []byte("file content"), "document.txt")
if err != nil {
fmt.Printf("Error saving file: %v\n", err)
return
}
fmt.Printf("File saved with ID: %s\n", fileID)
// 读取文件
data, err := storage.GetFile("user123", fileID)
if err != nil {
fmt.Printf("Error reading file: %v\n", err)
return
}
fmt.Printf("File content: %s\n", string(data))
}
gRPC服务集成
在gRPC服务中集成认证和存储:
package main
import (
"context"
"google.golang.org/grpc"
)
type WorldSimulationServer struct {
storage *FileStorage
}
func (s *WorldSimulationServer) SaveUserFile(ctx context.Context, req *SaveFileRequest) (*SaveFileResponse, error) {
// 认证已在拦截器中处理
fileID, err := s.storage.SaveFile(req.UserId, req.FileData, req.Filename)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &SaveFileResponse{FileId: fileID}, nil
}
func main() {
storage := NewFileStorage("./user_files")
interceptor := &AuthInterceptor{jwtSecret: []byte("your-secret-key")}
server := grpc.NewServer(
grpc.UnaryInterceptor(interceptor.UnaryInterceptor()),
)
simServer := &WorldSimulationServer{storage: storage}
RegisterWorldSimulationServer(server, simServer)
// 启动服务器...
}
这个方案提供了完整的认证流程和文件存储功能,避免了外部服务的依赖,可以直接集成到现有的gRPC应用中。JWT认证通过gRPC拦截器自动处理,文件存储使用本地文件系统,支持多用户和多文件存储。

