使用Golang实现带角色权限的用户认证功能
使用Golang实现带角色权限的用户认证功能 例如,我有5个HTML文件(首页、注册、登录、用户角色保密页面等)。
首先用户需要注册,我有7个输入字段(邮箱、姓名、姓氏、密码、角色等)。 在.go文件中,我从表单获取所有值并存入数据库。
请指导我如何通过会话或Cookie进行用户认证(登录页面),以及它们之间的区别是什么? 我尝试过不使用Cookie,仅通过SQL查询比对用户密码字段与数据库记录(但这种方式可能欠佳,且用户每次都需要重新登录才能查看保密页面)。
我还听说使用会话需要创建另一个数据库。 请告诉我关于注册、登录、用户认证和用户授权的最佳实践方案。
func main() {
fmt.Println("hello world")
}
更多关于使用Golang实现带角色权限的用户认证功能的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于使用Golang实现带角色权限的用户认证功能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
以下是基于Golang实现带角色权限的用户认证功能的完整解决方案,包括注册、登录、会话管理和授权控制。
1. 项目结构和依赖
首先安装必要的依赖:
go get github.com/gin-gonic/gin
go get golang.org/x/crypto/bcrypt
go get github.com/gorilla/sessions
go get github.com/mattn/go-sqlite3
2. 数据库模型
package main
import (
"database/sql"
"time"
_ "github.com/mattn/go-sqlite3"
"golang.org/x/crypto/bcrypt"
)
type User struct {
ID int `json:"id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Password string `json:"password"`
Role string `json:"role"`
CreatedAt time.Time `json:"created_at"`
}
type Session struct {
ID string `json:"id"`
UserID int `json:"user_id"`
ExpiresAt time.Time `json:"expires_at"`
}
func InitDB() (*sql.DB, error) {
db, err := sql.Open("sqlite3", "./users.db")
if err != nil {
return nil, err
}
// 创建用户表
userTable := `
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
password TEXT NOT NULL,
role TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`
// 创建会话表
sessionTable := `
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
user_id INTEGER NOT NULL,
expires_at DATETIME NOT NULL,
FOREIGN KEY(user_id) REFERENCES users(id)
)`
_, err = db.Exec(userTable)
if err != nil {
return nil, err
}
_, err = db.Exec(sessionTable)
return db, err
}
3. 密码加密和验证
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
func CheckPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
4. 注册功能
func RegisterHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var user struct {
Email string `form:"email" binding:"required,email"`
FirstName string `form:"first_name" binding:"required"`
LastName string `form:"last_name" binding:"required"`
Password string `form:"password" binding:"required,min=6"`
Role string `form:"role" binding:"required"`
}
if err := c.ShouldBind(&user); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 检查邮箱是否已存在
var count int
err := db.QueryRow("SELECT COUNT(*) FROM users WHERE email = ?", user.Email).Scan(&count)
if err != nil {
c.JSON(500, gin.H{"error": "Database error"})
return
}
if count > 0 {
c.JSON(400, gin.H{"error": "Email already exists"})
return
}
// 加密密码
hashedPassword, err := HashPassword(user.Password)
if err != nil {
c.JSON(500, gin.H{"error": "Failed to hash password"})
return
}
// 插入用户
result, err := db.Exec(
"INSERT INTO users (email, first_name, last_name, password, role) VALUES (?, ?, ?, ?, ?)",
user.Email, user.FirstName, user.LastName, hashedPassword, user.Role,
)
if err != nil {
c.JSON(500, gin.H{"error": "Failed to create user"})
return
}
userID, _ := result.LastInsertId()
c.JSON(200, gin.H{"message": "User created successfully", "user_id": userID})
}
}
5. 会话管理(使用数据库存储会话)
package main
import (
"crypto/rand"
"encoding/hex"
"time"
)
func GenerateSessionID() (string, error) {
bytes := make([]byte, 32)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}
func CreateSession(db *sql.DB, userID int) (string, error) {
sessionID, err := GenerateSessionID()
if err != nil {
return "", err
}
expiresAt := time.Now().Add(24 * time.Hour) // 24小时过期
_, err = db.Exec(
"INSERT INTO sessions (id, user_id, expires_at) VALUES (?, ?, ?)",
sessionID, userID, expiresAt,
)
if err != nil {
return "", err
}
return sessionID, nil
}
func GetUserFromSession(db *sql.DB, sessionID string) (*User, error) {
// 清理过期会话
db.Exec("DELETE FROM sessions WHERE expires_at < ?", time.Now())
var user User
err := db.QueryRow(`
SELECT u.id, u.email, u.first_name, u.last_name, u.role
FROM users u
JOIN sessions s ON u.id = s.user_id
WHERE s.id = ? AND s.expires_at > ?`,
sessionID, time.Now(),
).Scan(&user.ID, &user.Email, &user.FirstName, &user.LastName, &user.Role)
if err != nil {
return nil, err
}
return &user, nil
}
func DeleteSession(db *sql.DB, sessionID string) error {
_, err := db.Exec("DELETE FROM sessions WHERE id = ?", sessionID)
return err
}
6. 登录功能
func LoginHandler(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var credentials struct {
Email string `form:"email" binding:"required,email"`
Password string `form:"password" binding:"required"`
}
if err := c.ShouldBind(&credentials); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 查询用户
var user User
err := db.QueryRow(
"SELECT id, email, first_name, last_name, password, role FROM users WHERE email = ?",
credentials.Email,
).Scan(&user.ID, &user.Email, &user.FirstName, &user.LastName, &user.Password, &user.Role)
if err != nil {
c.JSON(401, gin.H{"error": "Invalid credentials"})
return
}
// 验证密码
if !CheckPasswordHash(credentials.Password, user.Password) {
c.JSON(401, gin.H{"error": "Invalid credentials"})
return
}
// 创建会话
sessionID, err := CreateSession(db, user.ID)
if err != nil {
c.JSON(500, gin.H{"error": "Failed to create session"})
return
}
// 设置Cookie
c.SetCookie("session_id", sessionID, 86400, "/", "", false, true)
c.JSON(200, gin.H{
"message": "Login successful",
"user": gin.H{
"id": user.ID,
"email": user.Email,
"first_name": user.FirstName,
"last_name": user.LastName,
"role": user.Role,
},
})
}
}
7. 认证中间件
func AuthMiddleware(db *sql.DB) gin.HandlerFunc {
return func(c *gin.Context) {
sessionID, err := c.Cookie("session_id")
if err != nil {
c.JSON(401, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
user, err := GetUserFromSession(db, sessionID)
if err != nil {
c.JSON(401, gin.H{"error": "Invalid session"})
c.Abort()
return
}
// 将用户信息存入上下文
c.Set("user", user)
c.Next()
}
}
8. 基于角色的授权中间件
func RoleMiddleware(allowedRoles ...string) gin.HandlerFunc {
return func(c *gin.Context) {
user, exists := c.Get("user")
if !exists {
c.JSON(401, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
currentUser := user.(*User)
// 检查用户角色是否在允许的角色列表中
for _, role := range allowedRoles {
if currentUser.Role == role {
c.Next()
return
}
}
c.JSON(403, gin.H{"error": "Insufficient permissions"})
c.Abort()
}
}
9. 主程序
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// 初始化数据库
db, err := InitDB()
if err != nil {
panic(err)
}
defer db.Close()
r := gin.Default()
// 静态文件服务
r.Static("/static", "./static")
r.LoadHTMLGlob("templates/*")
// 公开路由
r.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", nil)
})
r.GET("/register", func(c *gin.Context) {
c.HTML(200, "register.html", nil)
})
r.GET("/login", func(c *gin.Context) {
c.HTML(200, "login.html", nil)
})
// API路由
r.POST("/api/register", RegisterHandler(db))
r.POST("/api/login", LoginHandler(db))
// 需要认证的路由
auth := r.Group("/")
auth.Use(AuthMiddleware(db))
{
auth.GET("/profile", func(c *gin.Context) {
user, _ := c.Get("user")
c.HTML(200, "profile.html", gin.H{"user": user})
})
// 需要管理员权限的路由
admin := auth.Group("/admin")
admin.Use(RoleMiddleware("admin"))
{
admin.GET("/dashboard", func(c *gin.Context) {
c.HTML(200, "admin_dashboard.html", nil)
})
}
// 需要用户权限的路由
userRoute := auth.Group("/user")
userRoute.Use(RoleMiddleware("user", "admin"))
{
userRoute.GET("/dashboard", func(c *gin.Context) {
c.HTML(200, "user_dashboard.html", nil)
})
}
}
// 登出
r.POST("/api/logout", AuthMiddleware(db), func(c *gin.Context) {
sessionID, _ := c.Cookie("session_id")
DeleteSession(db, sessionID)
c.SetCookie("session_id", "", -1, "/", "", false, true)
c.JSON(200, gin.H{"message": "Logged out successfully"})
})
r.Run(":8080")
}
会话和Cookie的区别
Cookie:
- 存储在客户端浏览器中
- 有大小限制(约4KB)
- 每次请求都会自动发送到服务器
- 可以设置过期时间、域名、路径等属性
会话:
- 存储在服务器端(内存、数据库、Redis等)
- 通过Session ID与客户端关联
- Session ID通常通过Cookie传输
- 更安全,敏感数据不存储在客户端
在这个实现中,我们使用数据库存储会话数据,通过Cookie传输Session ID,结合了两者的优点:安全性(敏感数据在服务器端)和便利性(自动传输Session ID)。

