Golang中处理数据库和会话的最佳实践方法

Golang中处理数据库和会话的最佳实践方法 例如,我有4个HTML文件(首页、注册页、登录页、保密页面) 首先用户需要注册,我设置了7个输入字段(邮箱、姓名、姓氏、密码等) 在.go文件中,我获取表单中的所有值并将其存入数据库 请指导我如何通过会话或Cookie进行用户认证(登录页面),以及这两者之间的区别 我曾尝试不使用Cookie,仅通过SQL查询来比对用户密码字段与数据库记录(但这种方法可能欠佳,用户每次都需要重新登录才能查看保密页面) 我还听说实现会话需要创建另一个数据库 请告知关于注册、登录、用户认证和用户授权的最佳实践方案

1 回复

更多关于Golang中处理数据库和会话的最佳实践方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中处理数据库和会话认证时,推荐使用标准库database/sql配合gorilla/sessions包实现。以下是完整示例:

1. 数据库模型和会话配置

package main

import (
    "database/sql"
    "fmt"
    "log"
    "net/http"
    "time"
    
    "github.com/gorilla/sessions"
    "golang.org/x/crypto/bcrypt"
    _ "github.com/lib/pq" // PostgreSQL驱动
)

type User struct {
    ID        int
    Email     string
    FirstName string
    LastName  string
    Password  string
}

var (
    db          *sql.DB
    store       = sessions.NewCookieStore([]byte("your-secret-key"))
)

func init() {
    var err error
    db, err = sql.Open("postgres", "user=postgres dbname=myapp sslmode=disable")
    if err != nil {
        log.Fatal(err)
    }
}

2. 用户注册处理

func registerHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.ServeFile(w, r, "register.html")
        return
    }

    email := r.FormValue("email")
    firstName := r.FormValue("first_name")
    lastName := r.FormValue("last_name")
    password := r.FormValue("password")

    // 密码加密
    hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    if err != nil {
        http.Error(w, "密码处理错误", http.StatusInternalServerError)
        return
    }

    // 插入数据库
    _, err = db.Exec(
        "INSERT INTO users (email, first_name, last_name, password) VALUES ($1, $2, $3, $4)",
        email, firstName, lastName, string(hashedPassword),
    )
    if err != nil {
        http.Error(w, "注册失败", http.StatusInternalServerError)
        return
    }

    http.Redirect(w, r, "/login", http.StatusSeeOther)
}

3. 登录和会话创建

func loginHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.ServeFile(w, r, "login.html")
        return
    }

    email := r.FormValue("email")
    password := r.FormValue("password")

    var user User
    err := db.QueryRow(
        "SELECT id, email, password FROM users WHERE email = $1", email,
    ).Scan(&user.ID, &user.Email, &user.Password)
    
    if err == sql.ErrNoRows {
        http.Error(w, "用户不存在", http.StatusUnauthorized)
        return
    }

    // 验证密码
    err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password))
    if err != nil {
        http.Error(w, "密码错误", http.StatusUnauthorized)
        return
    }

    // 创建会话
    session, _ := store.Get(r, "user-session")
    session.Values["user_id"] = user.ID
    session.Values["authenticated"] = true
    session.Save(r, w)

    http.Redirect(w, r, "/secret", http.StatusSeeOther)
}

4. 认证中间件

func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        session, _ := store.Get(r, "user-session")
        
        if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
            http.Redirect(w, r, "/login", http.StatusSeeOther)
            return
        }
        
        next(w, r)
    }
}

func secretHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "欢迎访问保密页面!")
}

5. 路由设置

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "index.html")
    })
    http.HandleFunc("/register", registerHandler)
    http.HandleFunc("/login", loginHandler)
    http.HandleFunc("/secret", authMiddleware(secretHandler))
    
    log.Println("服务器启动在 :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

会话 vs Cookie 的区别

Cookie

  • 存储在客户端浏览器
  • 大小限制约4KB
  • 可设置过期时间
  • 示例:session, _ := store.Get(r, "session-name")

会话

  • 服务器端存储会话数据
  • 客户端只存储会话ID
  • 更安全,敏感数据不暴露给客户端
  • 使用Cookie存储会话ID

数据库表结构

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    first_name VARCHAR(100),
    last_name VARCHAR(100),
    password VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

此方案使用Gorilla Sessions管理会话,bcrypt加密密码,无需额外会话数据库。会话数据默认存储在Cookie中(加密),对于生产环境可配置Redis等存储后端。

回到顶部