微服务架构中,API接口规范的第一道防线是中间件。认证、限流、熔断、日志这些横切关注点通过中间件统一处理,避免业务代码重复实现。Spring Boot框架用拦截器和过滤器实现类似功能,Go语言Gin框架则用更轻量的中间件链。本文用Go实战演示JWT认证中间件和令牌桶限流中间件的完整实现。
Gin中间件机制
Gin中间件本质上是一个返回HandlerFunc的函数。通过c.Next()形成调用链,请求按注册顺序进入,响应按逆序返回。
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
// 日志中间件示例
func LoggerMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
method := c.Request.Method
// 请求前处理
c.Next()
// 请求后处理
latency := time.Since(start)
status := c.Writer.Status()
fmt.Printf("[%s] %s %s %d %v\n",
start.Format("2006-01-02 15:04:05"),
method, path, status, latency)
}
}
func main() {
r := gin.Default()
r.Use(LoggerMiddleware())
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "pong"})
})
r.Run(":8080")
}
JWT认证中间件实现
服务治理中,JWT(JSON Web Token)是无状态认证的主流方案。相比Session方案,JWT不需要服务端存储会话信息,天然适合分布式部署。
package middleware
import (
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
// JWT签名密钥(生产环境从配置中心读取)
var jwtSecret = []byte("your-secret-key-change-in-production")
// Claims 自定义JWT声明
type Claims struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
Role string `json:"role"`
jwt.RegisteredClaims
}
// GenerateToken 生成JWT令牌
func GenerateToken(userID int64, username, role string) (string, error) {
claims := Claims{
UserID: userID,
Username: username,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "yunthe-service",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}
// ParseToken 解析JWT令牌
func ParseToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{},
func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, jwt.ErrTokenInvalid
}
// JWTAuth JWT认证中间件
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
// 从Header获取Authorization
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "缺少认证信息",
})
c.Abort()
return
}
// 解析Bearer token
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "认证格式错误,应为Bearer Token",
})
c.Abort()
return
}
// 解析token
claims, err := ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{
"code": 401,
"message": "无效或过期的令牌",
})
c.Abort()
return
}
// 将用户信息存入上下文,后续handler可取用
c.Set("user_id", claims.UserID)
c.Set("username", claims.Username)
c.Set("role", claims.Role)
c.Next()
}
}
// RequireRole 角色权限校验中间件
func RequireRole(roles ...string) gin.HandlerFunc {
return func(c *gin.Context) {
userRole, exists := c.Get("role")
if !exists {
c.JSON(http.StatusForbidden, gin.H{
"code": 403,
"message": "无权限访问",
})
c.Abort()
return
}
roleStr := userRole.(string)
allowed := false
for _, r := range roles {
if roleStr == r {
allowed = true
break
}
}
if !allowed {
c.JSON(http.StatusForbidden, gin.H{
"code": 403,
"message": "权限不足",
})
c.Abort()
return
}
c.Next()
}
}
令牌桶限流中间件
高并发设计中,限流是保护服务的关键手段。令牌桶算法以固定速率生成令牌,请求消耗令牌,桶满时丢弃多余令牌。相比计数器方案,令牌桶允许一定程度的突发流量。
package middleware
import (
"net/http"
"sync"
"time"
"github.com/gin-gonic/gin"
)
// TokenBucket 令牌桶
type TokenBucket struct {
capacity float64 // 桶容量
rate float64 // 令牌生成速率(个/秒)
tokens float64 // 当前令牌数
lastRefill time.Time // 上次补充时间
mu sync.Mutex
}
// NewTokenBucket 创建令牌桶
func NewTokenBucket(capacity, rate float64) *TokenBucket {
return &TokenBucket{
capacity: capacity,
rate: rate,
tokens: capacity, // 初始满桶
lastRefill: time.Now(),
}
}
// Allow 尝试获取一个令牌
func (tb *TokenBucket) Allow() bool {
tb.mu.Lock()
defer tb.mu.Unlock()
now := time.Now()
// 计算自上次补充以来应该生成的令牌数
elapsed := now.Sub(tb.lastRefill).Seconds()
tb.tokens += elapsed * tb.rate
// 令牌不超过桶容量
if tb.tokens > tb.capacity {
tb.tokens = tb.capacity
}
tb.lastRefill = now
if tb.tokens >= 1 {
tb.tokens -= 1
return true
}
return false
}
// RateLimiter 基于IP的限流器
type RateLimiter struct {
buckets map[string]*TokenBucket
mu sync.RWMutex
capacity float64
rate float64
}
func NewRateLimiter(capacity, rate float64) *RateLimiter {
return &RateLimiter{
buckets: make(map[string]*TokenBucket),
capacity: capacity,
rate: rate,
}
}
func (rl *RateLimiter) GetBucket(key string) *TokenBucket {
rl.mu.RLock()
bucket, exists := rl.buckets[key]
rl.mu.RUnlock()
if exists {
return bucket
}
rl.mu.Lock()
defer rl.mu.Unlock()
// double-check
if bucket, exists := rl.buckets[key]; exists {
return bucket
}
bucket = NewTokenBucket(rl.capacity, rl.rate)
rl.buckets[key] = bucket
return bucket
}
// RateLimit 限流中间件
func RateLimit(capacity, rate float64) gin.HandlerFunc {
limiter := NewRateLimiter(capacity, rate)
return func(c *gin.Context) {
key := c.ClientIP()
bucket := limiter.GetBucket(key)
if !bucket.Allow() {
c.JSON(http.StatusTooManyRequests, gin.H{
"code": 429,
"message": "请求过于频繁,请稍后重试",
})
c.Abort()
return
}
c.Next()
}
}
熔断中间件
分布式事务中,下游服务故障时如果不及时熔断,级联失败会拖垮整个调用链。
package middleware
import (
"net/http"
"sync"
"time"
"github.com/gin-gonic/gin"
)
type CircuitState int
const (
StateClosed CircuitState = iota // 正常
StateOpen // 熔断
StateHalfOpen // 半开
)
type CircuitBreaker struct {
mu sync.Mutex
state CircuitState
failureCount int
failureThreshold int // 熔断阈值
resetTimeout time.Duration // 熔断恢复时间
lastFailure time.Time
successCount int
halfOpenMax int // 半开状态最大请求数
}
func NewCircuitBreaker(threshold int, resetTimeout time.Duration) *CircuitBreaker {
return &CircuitBreaker{
state: StateClosed,
failureThreshold: threshold,
resetTimeout: resetTimeout,
halfOpenMax: 3,
}
}
func (cb *CircuitBreaker) Allow() bool {
cb.mu.Lock()
defer cb.mu.Unlock()
switch cb.state {
case StateClosed:
return true
case StateOpen:
if time.Since(cb.lastFailure) > cb.resetTimeout {
cb.state = StateHalfOpen
cb.successCount = 0
return true
}
return false
case StateHalfOpen:
return cb.successCount < cb.halfOpenMax
}
return false
}
func (cb *CircuitBreaker) RecordSuccess() {
cb.mu.Lock()
defer cb.mu.Unlock()
if cb.state == StateHalfOpen {
cb.successCount++
if cb.successCount >= cb.halfOpenMax {
cb.state = StateClosed
cb.failureCount = 0
}
}
}
func (cb *CircuitBreaker) RecordFailure() {
cb.mu.Lock()
defer cb.mu.Unlock()
cb.lastFailure = time.Now()
cb.failureCount++
if cb.state == StateHalfOpen {
cb.state = StateOpen
return
}
if cb.failureCount >= cb.failureThreshold {
cb.state = StateOpen
}
}
// CircuitBreakerMiddleware 熔断中间件
func CircuitBreakerMiddleware(cb *CircuitBreaker) gin.HandlerFunc {
return func(c *gin.Context) {
if !cb.Allow() {
c.JSON(http.StatusServiceUnavailable, gin.H{
"code": 503,
"message": "服务暂时不可用,请稍后重试",
})
c.Abort()
return
}
c.Next()
if c.Writer.Status() >= 500 {
cb.RecordFailure()
} else {
cb.RecordSuccess()
}
}
}
中间件组合使用
func main() {
r := gin.Default()
// 全局中间件
r.Use(LoggerMiddleware())
r.Use(RateLimit(100, 10)) // 桶容量100,每秒10个令牌
cb := NewCircuitBreaker(5, 30*time.Second)
// 公开接口
public := r.Group("/api/v1")
public.Use(CircuitBreakerMiddleware(cb))
{
public.POST("/login", loginHandler)
public.POST("/register", registerHandler)
}
// 需要认证的接口
auth := r.Group("/api/v1")
auth.Use(CircuitBreakerMiddleware(cb))
auth.Use(JWTAuth())
{
auth.GET("/profile", profileHandler)
auth.PUT("/profile", updateProfileHandler)
// 管理员接口
admin := auth.Group("/admin")
admin.Use(RequireRole("admin"))
{
admin.GET("/users", listUsersHandler)
admin.DELETE("/users/:id", deleteUserHandler)
}
}
r.Run(":8080")
}
消息中间件配合限流熔断,构成微服务的防护体系。中间件的执行顺序直接影响防护效果——限流应在认证之前(拦截无效请求),熔断应在业务逻辑之前(快速失败)。业务中台建设中,这套模式可作为标准基础设施复用。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/go-yu-yan-gin-kuang-jia-zhong-jian-jian-she-ji-jwt-ren/