在 Web 开发中,用户身份验证是确保应用安全性的重要环节。本文将全面讲解如何在 Golang 中利用 session cookie 实现用户身份验证。通过该教程,您将学会如何创建登录、会话验证和用户登出等功能,使应用更安全且用户体验更友好。

Session cookie 是一种存储在用户浏览器中的小型数据包,用于在用户会话期间维护其状态。当用户登录成功后,服务器会创建一个 session,将其信息保存为 cookie,并在后续请求中使用该 cookie 进行身份验证。

项目概览

本教程将带您创建一个简单的 Golang 应用程序,包含以下两个核心路由:

  • /signin 路由:接收用户名和密码,验证成功后设置 session cookie。
  • /welcome 路由:显示用户的个性化欢迎消息。

在开发阶段,我们将使用内存来存储会话信息,以便更好地理解原理。实际生产环境中,建议将会话存储在数据库或分布式缓存系统中以提高可靠性。

2. Golang HTTP 服务器配置

首先,我们需要初始化一个 HTTP 服务器,并定义基本路由:

package main

import (
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("/signin", Signin)
	http.HandleFunc("/welcome", Welcome)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

3. 创建用户会话逻辑

用户登录后,我们将创建一个唯一的会话令牌。为简便起见,我们将使用内存中的 map 来存储用户凭证和会话信息。

var users = map[string]string{
	"user1": "password1",
	"user2": "password2",
}

var sessions = map[string]session{}

type session struct {
	username string
	expiry   time.Time
}

func (s session) isExpired() bool {
	return s.expiry.Before(time.Now())
}

定义 Signin 处理程序,用于接收用户登录请求并在成功后创建一个 session cookie:

type Credentials struct {
	Password string `json:"password"`
	Username string `json:"username"`
}

func Signin(w http.ResponseWriter, r *http.Request) {
	var creds Credentials
	err := json.NewDecoder(r.Body).Decode(&creds)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	expectedPassword, ok := users[creds.Username]
	if !ok || expectedPassword != creds.Password {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	sessionToken := uuid.NewString()
	expiresAt := time.Now().Add(120 * time.Second)

	sessions[sessionToken] = session{
		username: creds.Username,
		expiry:   expiresAt,
	}

	http.SetCookie(w, &http.Cookie{
		Name:    "session_token",
		Value:   sessionToken,
		Expires: expiresAt,
	})
}

通过上述代码,服务器会生成一个 session_token 并存储在 cookie 中。用户请求时,该 session cookie 将随之发送,便于在后续请求中对其身份进行验证。

5. 验证用户身份

接下来实现 Welcome 处理程序,验证用户的 session_token:

func Welcome(w http.ResponseWriter, r *http.Request) {
	c, err := r.Cookie("session_token")
	if err != nil {
		if err == http.ErrNoCookie {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	sessionToken := c.Value
	userSession, exists := sessions[sessionToken]
	if !exists || userSession.isExpired() {
		delete(sessions, sessionToken)
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	w.Write([]byte(fmt.Sprintf("Welcome %s!", userSession.username)))
}

Welcome 处理程序会检查用户的 session 是否有效。如果 session 不存在或已过期,用户将会收到 401 未授权响应。

6. 刷新 Session Token:保持登录状态

为确保用户长时间在线,我们需要刷新其 session token。新增一个 /refresh 路由,用于在会话即将过期时更新 token:

func Refresh(w http.ResponseWriter, r *http.Request) {
	c, err := r.Cookie("session_token")
	if err != nil {
		if err == http.ErrNoCookie {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	sessionToken := c.Value
	userSession, exists := sessions[sessionToken]
	if !exists || userSession.isExpired() {
		delete(sessions, sessionToken)
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	newSessionToken := uuid.NewString()
	expiresAt := time.Now().Add(120 * time.Second)

	sessions[newSessionToken] = session{
		username: userSession.username,
		expiry:   expiresAt,
	}

	delete(sessions, sessionToken)

	http.SetCookie(w, &http.Cookie{
		Name:    "session_token",
		Value:   newSessionToken,
		Expires: expiresAt,
	})
}

7. 用户登出:清除会话

实现 Logout 处理程序,便于用户退出时删除服务器和客户端的会话信息:

func Logout(w http.ResponseWriter, r *http.Request) {
	c, err := r.Cookie("session_token")
	if err != nil {
		if err == http.ErrNoCookie {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	sessionToken := c.Value

	delete(sessions, sessionToken)

	http.SetCookie(w, &http.Cookie{
		Name:    "session_token",
		Value:   "",
		Expires: time.Now(),
	})
}

8. 启动与测试应用程序

通过命令行构建并运行该应用程序,使用支持 cookie 的 HTTP 客户端(如 Postman 或浏览器)测试以下功能:

  • 登录POST http://localhost:8080/signin
  • 获取欢迎消息GET http://localhost:8080/welcome
  • 刷新会话POST http://localhost:8080/refresh
  • 登出GET http://localhost:8080/logout

总结

本教程演示了如何在 Golang 应用中使用 session cookie 实现用户身份验证。随着应用的发展,可进一步增强数据存储的安全性和一致性,以确保用户数据的安全和隐私。

相关阅读推荐:使用 Golang 构建基于 JWT 的身份验证系统


也可以看看