在实际 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
三、完整代码
package mainimport ("crypto/rand""encoding/hex""fmt""net/http""sync"
)var (users = make(map[string]string) // 用户存储: username -> passwordsessions = make(map[string]string) // 会话存储: session_id -> usernamemu 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] = passwordfmt.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}// 创建 SessionsessionID := generateSessionID()mu.Lock()sessions[sessionID] = usernamemu.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 run main.go
- 注册用户
curl -X POST -d "username=tom&password=123456" http://localhost:8080/register
返回:
注册成功: tom
- 登录用户
curl -i -X POST -d "username=tom&password=123456" http://localhost:8080/login
返回(同时设置了 Set-Cookie
):
登录成功: tom
- 获取当前用户信息
curl --cookie "session_id=xxxxx" http://localhost:8080/profile
返回:
当前登录用户: tom
五、注意事项
-
内存存储:
- 服务器重启后数据会丢失
- 不适合生产环境
-
安全性:
- 真实环境必须加密存储密码(如
bcrypt
) - 建议使用 HTTPS 保护 Cookie
- 真实环境必须加密存储密码(如
-
Session 过期:
- 生产环境要设置 Session 过期时间并定期清理
六、进阶扩展
- 使用
bcrypt
对密码进行哈希处理 - 将用户数据和 Session 存储到 Redis 或 数据库
- 添加 登出接口 清除 Session
- 使用第三方 Session 管理库(如
gorilla/sessions
)