在实际 Web 应用中,用户注册与登录 是最常见的功能之一。
本案例不使用数据库,而是将用户信息存储在内存中,主要用于学习和演示。
一、功能目标
-
- 注册接口
/register
- 注册接口
-
- • 提交用户名和密码
- • 保存到内存 map 中
- • 返回注册成功信息
-
- 登录接口
/login
- 登录接口
-
- • 验证用户名和密码
- • 登录成功后,创建 Session
- • 在 Cookie 中存储
session_id
-
- 用户信息接口
/profile
- 用户信息接口
-
- • 读取 Cookie 中的 Session
- • 返回当前登录用户信息
二、核心知识点
- • 表单数据解析 :
r.FormValue("username") - • 内存用户存储 :
map[string]string(用户名 → 密码) - • Session 管理:
-
- •
map[string]string(session_id → username) - • Cookie 存储 Session ID
- •
- • 随机 Session ID 生成 :
crypto/rand+encoding/hex
三、完整代码
go
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
"sync"
)
var (
users = make(map[string]string) // 用户存储: username -> password
sessions = make(map[string]string) // 会话存储: session_id -> username
mu sync.Mutex
)
// 生成随机 Session ID
func generateSessionID() string {
b := make([]byte, 16)
rand.Read(b)
return hex.EncodeToString(b)
}
// 注册
func registerHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "只支持 POST", http.StatusMethodNotAllowed)
return
}
username := r.FormValue("username")
password := r.FormValue("password")
if username == "" || password == "" {
http.Error(w, "用户名和密码不能为空", http.StatusBadRequest)
return
}
mu.Lock()
defer mu.Unlock()
if _, exists := users[username]; exists {
http.Error(w, "用户已存在", http.StatusConflict)
return
}
users[username] = password
fmt.Fprintf(w, "注册成功: %s\n", username)
}
// 登录
func loginHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "只支持 POST", http.StatusMethodNotAllowed)
return
}
username := r.FormValue("username")
password := r.FormValue("password")
mu.Lock()
storedPwd, exists := users[username]
mu.Unlock()
if !exists || storedPwd != password {
http.Error(w, "用户名或密码错误", http.StatusUnauthorized)
return
}
// 创建 Session
sessionID := generateSessionID()
mu.Lock()
sessions[sessionID] = username
mu.Unlock()
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: sessionID,
Path: "/",
})
fmt.Fprintf(w, "登录成功: %s\n", username)
}
// 获取用户信息
func profileHandler(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("session_id")
if err != nil {
http.Error(w, "未登录", http.StatusUnauthorized)
return
}
mu.Lock()
username, exists := sessions[cookie.Value]
mu.Unlock()
if !exists {
http.Error(w, "会话无效", http.StatusUnauthorized)
return
}
fmt.Fprintf(w, "当前登录用户: %s\n", username)
}
func main() {
http.HandleFunc("/register", registerHandler)
http.HandleFunc("/login", loginHandler)
http.HandleFunc("/profile", profileHandler)
fmt.Println("服务器已启动:http://localhost:8080")
http.ListenAndServe(":8080", nil)
}
四、运行与测试
-
- 启动服务器
go
go run main.go
-
- 注册用户
bash
curl -X POST -d "username=tom&password=123456" http://localhost:8080/register
返回:
makefile
注册成功: tom
-
- 登录用户
bash
curl -i -X POST -d "username=tom&password=123456" http://localhost:8080/login
返回(同时设置了 Set-Cookie):
makefile
登录成功: tom
-
- 获取当前用户信息
bash
curl --cookie "session_id=xxxxx" http://localhost:8080/profile
返回:
makefile
当前登录用户: tom
五、注意事项
- • 内存存储:
-
- • 服务器重启后数据会丢失
- • 不适合生产环境
- • 安全性:
-
- • 真实环境必须加密存储密码(如
bcrypt) - • 建议使用 HTTPS 保护 Cookie
- • 真实环境必须加密存储密码(如
- • Session 过期:
-
- • 生产环境要设置 Session 过期时间并定期清理
六、进阶扩展
- • 使用
bcrypt对密码进行哈希处理 - • 将用户数据和 Session 存储到 Redis 或 数据库
- • 添加 登出接口 清除 Session
- • 使用第三方 Session 管理库(如
gorilla/sessions)