Golang中创建用户登录功能的实现

Golang中创建用户登录功能的实现 能否请您帮助我使用会话和数据库创建用户登录、登出、注册和权限功能。

3 回复

非常感谢,这非常有帮助

更多关于Golang中创建用户登录功能的实现的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


看看这个代码库

GitHub

GitHub

头像

geosoft1/ssas

Go语言的服务器端应用程序骨架。通过在GitHub上创建账户来为geosoft1/ssas开发做贡献。

以下是使用Golang实现用户登录、注册、登出和权限管理功能的完整示例。我们将使用Gin框架作为HTTP路由器,GORM作为ORM工具,以及JWT(JSON Web Tokens)来处理会话管理。数据库使用SQLite,但你可以轻松替换为MySQL或PostgreSQL。

1. 项目结构和依赖

首先,创建项目结构并安装必要的依赖:

go mod init auth-example
go get github.com/gin-gonic/gin
go get gorm.io/gorm
go get gorm.io/driver/sqlite
go get github.com/golang-jwt/jwt/v4
go get golang.org/x/crypto/bcrypt

2. 数据库模型定义

models/user.go中定义用户模型:

package models

import (
	"gorm.io/gorm"
	"golang.org/x/crypto/bcrypt"
)

type User struct {
	gorm.Model
	Username string `gorm:"uniqueIndex;not null" json:"username"`
	Password string `gorm:"not null" json:"-"`
	Role     string `gorm:"default:user" json:"role"` // 权限角色:user, admin
}

// HashPassword 对密码进行哈希处理
func (u *User) HashPassword() error {
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	u.Password = string(hashedPassword)
	return nil
}

// CheckPassword 验证密码
func (u *User) CheckPassword(password string) error {
	return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
}

3. 数据库初始化

database/database.go中初始化数据库:

package database

import (
	"auth-example/models"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

var DB *gorm.DB

func InitDatabase() error {
	var err error
	DB, err = gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
	if err != nil {
		return err
	}

	// 自动迁移模型
	err = DB.AutoMigrate(&models.User{})
	if err != nil {
		return err
	}

	return nil
}

4. JWT工具函数

utils/jwt.go中处理JWT的生成和验证:

package utils

import (
	"time"
	"github.com/golang-jwt/jwt/v4"
)

var jwtSecret = []byte("your-secret-key") // 在生产环境中使用环境变量

type Claims struct {
	UserID   uint   `json:"user_id"`
	Username string `json:"username"`
	Role     string `json:"role"`
	jwt.RegisteredClaims
}

func GenerateToken(userID uint, username, role string) (string, error) {
	expirationTime := time.Now().Add(24 * time.Hour)
	claims := &Claims{
		UserID:   userID,
		Username: username,
		Role:     role,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expirationTime),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(jwtSecret)
}

func ValidateToken(tokenString string) (*Claims, error) {
	claims := &Claims{}
	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
		return jwtSecret, nil
	})

	if err != nil {
		return nil, err
	}

	if !token.Valid {
		return nil, jwt.ErrSignatureInvalid
	}

	return claims, nil
}

5. 中间件

middleware/auth.go中创建认证和授权中间件:

package middleware

import (
	"auth-example/utils"
	"net/http"
	"strings"
	"github.com/gin-gonic/gin"
)

func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
			c.Abort()
			return
		}

		tokenString := strings.TrimPrefix(authHeader, "Bearer ")
		claims, err := utils.ValidateToken(tokenString)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
			c.Abort()
			return
		}

		c.Set("userID", claims.UserID)
		c.Set("username", claims.Username)
		c.Set("role", claims.Role)
		c.Next()
	}
}

func RoleMiddleware(allowedRoles ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		userRole, exists := c.Get("role")
		if !exists {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "User role not found"})
			c.Abort()
			return
		}

		roleStr := userRole.(string)
		for _, role := range allowedRoles {
			if roleStr == role {
				c.Next()
				return
			}
		}

		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		c.Abort()
	}
}

6. 路由和处理函数

main.go中定义路由和处理函数:

package main

import (
	"auth-example/database"
	"auth-example/middleware"
	"auth-example/models"
	"auth-example/utils"
	"net/http"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

func main() {
	// 初始化数据库
	if err := database.InitDatabase(); err != nil {
		panic("Failed to connect to database: " + err.Error())
	}

	r := gin.Default()

	// 注册路由
	r.POST("/register", register)
	r.POST("/login", login)

	// 需要认证的路由组
	auth := r.Group("/")
	auth.Use(middleware.AuthMiddleware())
	{
		auth.GET("/profile", profile)
		auth.POST("/logout", logout)
		
		// 需要管理员权限的路由
		admin := auth.Group("/admin")
		admin.Use(middleware.RoleMiddleware("admin"))
		{
			admin.GET("/users", getUsers)
		}
	}

	r.Run(":8080")
}

// 注册处理函数
func register(c *gin.Context) {
	var user models.User
	if err := c.ShouldBindJSON(&user); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// 哈希密码
	if err := user.HashPassword(); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not hash password"})
		return
	}

	// 保存用户到数据库
	result := database.DB.Create(&user)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not create user"})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"message": "User created successfully",
		"user": gin.H{
			"id":       user.ID,
			"username": user.Username,
			"role":     user.Role,
		},
	})
}

// 登录处理函数
func login(c *gin.Context) {
	var loginData struct {
		Username string `json:"username" binding:"required"`
		Password string `json:"password" binding:"required"`
	}

	if err := c.ShouldBindJSON(&loginData); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	var user models.User
	result := database.DB.Where("username = ?", loginData.Username).First(&user)
	if result.Error != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
		return
	}

	// 验证密码
	if err := user.CheckPassword(loginData.Password); err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid credentials"})
		return
	}

	// 生成JWT令牌
	token, err := utils.GenerateToken(user.ID, user.Username, user.Role)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not generate token"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Login successful",
		"token":   token,
		"user": gin.H{
			"id":       user.ID,
			"username": user.Username,
			"role":     user.Role,
		},
	})
}

// 用户信息处理函数
func profile(c *gin.Context) {
	userID, _ := c.Get("userID")
	username, _ := c.Get("username")
	role, _ := c.Get("role")

	c.JSON(http.StatusOK, gin.H{
		"user": gin.H{
			"id":       userID,
			"username": username,
			"role":     role,
		},
	})
}

// 登出处理函数
func logout(c *gin.Context) {
	// 在实际应用中,你可能需要将令牌加入黑名单
	// 这里只是返回成功消息
	c.JSON(http.StatusOK, gin.H{"message": "Logout successful"})
}

// 获取所有用户(仅管理员)
func getUsers(c *gin.Context) {
	var users []models.User
	result := database.DB.Find(&users)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not fetch users"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"users": users})
}

7. 使用示例

注册用户:

curl -X POST http://localhost:8080/register \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","password":"password123","role":"user"}'

登录:

curl -X POST http://localhost:8080/login \
  -H "Content-Type: application/json" \
  -d '{"username":"testuser","password":"password123"}'

访问受保护的路由:

curl -X GET http://localhost:8080/profile \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

管理员访问用户列表:

curl -X GET http://localhost:8080/admin/users \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"

这个实现提供了完整的用户认证和授权功能,包括密码哈希、JWT会话管理和基于角色的权限控制。你可以根据需要扩展这个基础实现,比如添加令牌刷新、密码重置、电子邮件验证等功能。

回到顶部