如何将服务开放给用户——构建 API 接口和用户认证的实践指南

58 阅读2分钟

在基于 Go 的项目中,为用户提供服务通常需要构建 RESTful 或 gRPC API,并实现用户认证来保护服务。以下是实践中的关键步骤和示例代码。


一、设计你的 API 接口

1. 确定 API 的目标和功能

  • 明确服务的核心功能,例如用户注册、登录、数据操作等。

  • 设计 RESTful 风格的接口。例如:

    • POST /register 用于用户注册
    • POST /login 用于用户登录
    • GET /profile 用于获取用户信息

2. 使用 Go 的 net/http 构建简单的 RESTful API

Go 内置了强大的 HTTP 支持,通过 net/http 包可以快速实现 API。

示例代码:

package main

import (
	"encoding/json"
	"net/http"
)

type User struct {
	ID       int    `json:"id"`
	Username string `json:"username"`
	Password string `json:"-"`
}

var users = []User{
	{ID: 1, Username: "testuser", Password: "password123"},
}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var newUser User
	err := json.NewDecoder(r.Body).Decode(&newUser)
	if err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	newUser.ID = len(users) + 1
	users = append(users, newUser)

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(newUser)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

	var creds User
	err := json.NewDecoder(r.Body).Decode(&creds)
	if err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	for _, user := range users {
		if user.Username == creds.Username && user.Password == creds.Password {
			http.SetCookie(w, &http.Cookie{
				Name:  "session_token",
				Value: "token123", // 应用中应该生成一个随机的 JWT 或其他令牌
				Path:  "/",
			})
			w.WriteHeader(http.StatusOK)
			w.Write([]byte("Login successful"))
			return
		}
	}

	http.Error(w, "Invalid credentials", http.StatusUnauthorized)
}

func main() {
	http.HandleFunc("/register", registerHandler)
	http.HandleFunc("/login", loginHandler)

	http.ListenAndServe(":8080", nil)
}

二、添加用户认证功能

1. 使用 JWT(JSON Web Token)进行认证

JWT 是一种常用的用户认证方案。登录成功后生成一个 JWT,用户在后续请求中携带此令牌。

示例代码:

import (
	"github.com/golang-jwt/jwt/v5"
	"time"
)

var jwtKey = []byte("secret_key")

func generateJWT(username string) (string, error) {
	claims := &jwt.MapClaims{
		"username": username,
		"exp":      time.Now().Add(1 * time.Hour).Unix(),
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString(jwtKey)
}

func authenticateJWT(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		tokenStr := r.Header.Get("Authorization")
		if tokenStr == "" {
			http.Error(w, "Missing token", http.StatusUnauthorized)
			return
		}

		claims := &jwt.MapClaims{}
		token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
			return jwtKey, nil
		})

		if err != nil || !token.Valid {
			http.Error(w, "Invalid token", http.StatusUnauthorized)
			return
		}

		// 用户信息在 claims 中
		r.Header.Set("username", (*claims)["username"].(string))
		next(w, r)
	}
}

2. 在受保护的路由中使用 JWT 认证

通过中间件保护某些 API 路由。

示例代码:

func profileHandler(w http.ResponseWriter, r *http.Request) {
	username := r.Header.Get("username")
	w.Write([]byte("Welcome, " + username))
}

func main() {
	http.HandleFunc("/register", registerHandler)
	http.HandleFunc("/login", loginHandler)
	http.HandleFunc("/profile", authenticateJWT(profileHandler))

	http.ListenAndServe(":8080", nil)
}

三、构建生产级 API

1. 结构化项目代码

将代码拆分为模块:

  • handlers:存放路由和处理逻辑
  • models:存放数据结构和数据库操作
  • middlewares:存放认证逻辑
  • routes:存放路由配置

2. 使用框架提升开发效率

推荐使用框架,如 GinEcho,它们提供更便捷的路由、请求处理和中间件支持。

使用 Gin 的示例:

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()

	r.POST("/register", func(c *gin.Context) {
		// 注册逻辑
		c.JSON(http.StatusOK, gin.H{"message": "User registered"})
	})

	r.POST("/login", func(c *gin.Context) {
		// 登录逻辑
		c.JSON(http.StatusOK, gin.H{"message": "User logged in"})
	})

	r.GET("/profile", func(c *gin.Context) {
		// 需要认证的逻辑
		c.JSON(http.StatusOK, gin.H{"username": "testuser"})
	})

	r.Run(":8080")
}

四、测试和优化

1. 测试你的 API

  • 使用工具如 PostmancURL 测试 API。
  • 编写单元测试和集成测试,确保 API 的稳定性。

2. 优化你的 API

  • 实现分页和过滤,提升数据查询效率。
  • 使用缓存(如 Redis)减少数据库查询次数。
  • 添加速率限制,防止滥用 API。