在基于 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. 使用框架提升开发效率
推荐使用框架,如 Gin 或 Echo,它们提供更便捷的路由、请求处理和中间件支持。
使用 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
2. 优化你的 API
- 实现分页和过滤,提升数据查询效率。
- 使用缓存(如 Redis)减少数据库查询次数。
- 添加速率限制,防止滥用 API。