HiHuo
首页
博客
手册
工具
关于
首页
博客
手册
工具
关于
  • SSO与身份认证

    • 单点登录(SSO)与身份认证教程
    • 第1章:认证与授权基础
    • 第2章:单点登录原理与实现
    • 第3章:OAuth 2.0与OpenID Connect
    • 第4章:企业级SSO方案
    • 第5章:微服务认证与安全

第5章:微服务认证与安全

微服务认证架构

集中式认证

┌──────────┐                 ┌─────────────┐
│  Client  │──────────────────>│ API Gateway │
└──────────┘                 └─────────────┘
                                     │
                              1. 验证JWT
                              2. 转发请求
                                     │
         ┌───────────────────────────┼───────────────────────────┐
         │                           │                           │
         ↓                           ↓                           ↓
    ┌────────┐                  ┌────────┐                 ┌────────┐
    │Service │                  │Service │                 │Service │
    │   A    │                  │   B    │                 │   C    │
    └────────┘                  └────────┘                 └────────┘
    (无需验证)                (无需验证)              (无需验证)

优势:
 统一认证
 服务简单
 易于管理

劣势:
 Gateway成为瓶颈
 单点故障

分布式认证

┌──────────┐                 ┌─────────────┐
│  Client  │──────────────────>│ API Gateway │
└──────────┘                 └─────────────┘
                                     │
                              1. 传递JWT
                                     │
         ┌───────────────────────────┼───────────────────────────┐
         │                           │                           │
         ↓                           ↓                           ↓
    ┌────────┐                  ┌────────┐                 ┌────────┐
    │Service │                  │Service │                 │Service │
    │   A    │                  │   B    │                 │   C    │
    │验证JWT │                  │验证JWT │                 │验证JWT │
    └────────┘                  └────────┘                 └────────┘

优势:
 无单点故障
 扩展性好

劣势:
 各服务需要验证逻辑
 密钥分发复杂

API Gateway统一认证

Kong + JWT插件

安装Kong:

docker run -d --name kong \
  -e "KONG_DATABASE=off" \
  -e "KONG_DECLARATIVE_CONFIG=/kong/declarative/kong.yml" \
  -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
  -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
  -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
  -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
  -p 8000:8000 \
  -p 8443:8443 \
  -p 8001:8001 \
  -p 8444:8444 \
  kong:latest

配置JWT插件:

# 添加服务
curl -X POST http://localhost:8001/services \
  --data name=user-service \
  --data url=http://user-service:8080

# 添加路由
curl -X POST http://localhost:8001/services/user-service/routes \
  --data paths[]=/api/users

# 启用JWT插件
curl -X POST http://localhost:8001/services/user-service/plugins \
  --data name=jwt

# 创建消费者
curl -X POST http://localhost:8001/consumers \
  --data username=myapp

# 为消费者添加JWT凭证
curl -X POST http://localhost:8001/consumers/myapp/jwt \
  --data key=myapp-key \
  --data secret=my-secret-key

使用JWT访问:

// 生成JWT
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
	"iss": "myapp-key",
	"exp": time.Now().Add(1 * time.Hour).Unix(),
})
tokenString, _ := token.SignedString([]byte("my-secret-key"))

// 访问API
req, _ := http.NewRequest("GET", "http://localhost:8000/api/users", nil)
req.Header.Set("Authorization", "Bearer "+tokenString)
resp, _ := http.DefaultClient.Do(req)

服务间认证

方案1:共享JWT密钥

// 所有服务使用相同密钥验证JWT
var jwtSecret = []byte("shared-secret-key")

func AuthMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tokenString := extractToken(r)

		token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			return jwtSecret, nil
		})

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

		next.ServeHTTP(w, r)
	})
}

方案2:Service Account

// 服务A调用服务B
type ServiceClient struct {
	serviceID     string
	serviceSecret string
	authURL       string
}

func (c *ServiceClient) GetAccessToken() (string, error) {
	// 用服务凭证获取Token
	resp, _ := http.PostForm(c.authURL+"/token", url.Values{
		"grant_type":    {"client_credentials"},
		"client_id":     {c.serviceID},
		"client_secret": {c.serviceSecret},
	})

	var result struct {
		AccessToken string `json:"access_token"`
	}
	json.NewDecoder(resp.Body).Decode(&result)

	return result.AccessToken, nil
}

func (c *ServiceClient) CallServiceB() error {
	// 获取Token
	token, _ := c.GetAccessToken()

	// 调用服务B
	req, _ := http.NewRequest("GET", "http://service-b/api/data", nil)
	req.Header.Set("Authorization", "Bearer "+token)
	resp, _ := http.DefaultClient.Do(req)

	return nil
}

方案3:mTLS(双向TLS)

// 服务A配置mTLS
func NewMTLSClient(certFile, keyFile, caFile string) (*http.Client, error) {
	// 加载客户端证书
	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return nil, err
	}

	// 加载CA证书
	caCert, _ := os.ReadFile(caFile)
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	// 配置TLS
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      caCertPool,
	}

	// 创建HTTP客户端
	return &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: tlsConfig,
		},
	}, nil
}

// 使用
client, _ := NewMTLSClient("client.crt", "client.key", "ca.crt")
resp, _ := client.Get("https://service-b/api/data")

安全最佳实践

1. XSS防护

// 设置安全Header
func SecurityHeaders(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("X-Content-Type-Options", "nosniff")
		w.Header().Set("X-Frame-Options", "DENY")
		w.Header().Set("X-XSS-Protection", "1; mode=block")
		w.Header().Set("Content-Security-Policy", "default-src 'self'")
		next.ServeHTTP(w, r)
	})
}

// 转义用户输入
import "html"

func SafeOutput(input string) string {
	return html.EscapeString(input)
}

2. CSRF防护

// CSRF Token生成
func GenerateCSRFToken() string {
	bytes := make([]byte, 32)
	rand.Read(bytes)
	return base64.URLEncoding.EncodeToString(bytes)
}

// CSRF中间件
func CSRFMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "GET" {
			token := r.Header.Get("X-CSRF-Token")
			sessionToken := getSessionCSRFToken(r)

			if token != sessionToken {
				http.Error(w, "Invalid CSRF token", 403)
				return
			}
		}

		next.ServeHTTP(w, r)
	})
}

3. SQL注入防护

//  错误:拼接SQL
query := fmt.Sprintf("SELECT * FROM users WHERE username='%s'", username)
db.Query(query)

//  正确:使用参数化查询
db.Query("SELECT * FROM users WHERE username=?", username)

4. 敏感信息加密

// 加密敏感字段
import "crypto/aes"
import "crypto/cipher"

func Encrypt(plaintext, key []byte) ([]byte, error) {
	block, _ := aes.NewCipher(key)
	gcm, _ := cipher.NewGCM(block)
	nonce := make([]byte, gcm.NonceSize())
	rand.Read(nonce)

	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
	return ciphertext, nil
}

func Decrypt(ciphertext, key []byte) ([]byte, error) {
	block, _ := aes.NewCipher(key)
	gcm, _ := cipher.NewGCM(block)

	nonceSize := gcm.NonceSize()
	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]

	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	return plaintext, err
}

完整方案

微服务SSO完整架构

┌──────────┐
│ Browser  │
└──────────┘
     │
     │ 1. 访问应用
     ↓
┌─────────────┐
│ API Gateway │ ← JWT验证
│  (Kong)     │
└─────────────┘
     │
     │ 2. 转发请求(携带JWT)
     │
     ├───────────────┬───────────────┐
     ↓               ↓               ↓
┌────────┐      ┌────────┐     ┌────────┐
│ User   │      │ Order  │     │Product │
│Service │      │Service │     │Service │
└────────┘      └────────┘     └────────┘
     │               │              │
     └───────────────┴──────────────┘
              │
              ↓
      ┌──────────────┐
      │  PostgreSQL  │
      └──────────────┘

认证流程:
1. 用户登录 → Keycloak
2. Keycloak返回JWT
3. 客户端携带JWT访问API Gateway
4. API Gateway验证JWT
5. 转发请求到微服务
6. 微服务处理业务(可选:再次验证JWT)

面试问答

微服务中如何实现统一认证?

答案:

方案1:API Gateway统一认证

优点:
 统一认证逻辑
 服务无需关心认证
 易于管理

缺点:
 Gateway成为瓶颈
 单点故障

适用:
 中小规模
 对延迟不敏感

方案2:分布式验证

优点:
 无单点故障
 扩展性好

缺点:
 各服务需要验证逻辑
 密钥管理复杂

适用:
 大规模
 高可用要求

参考资料

  • OAuth 2.0 RFC 6749
  • OpenID Connect
  • Keycloak Documentation
  • OWASP Top 10
Prev
第4章:企业级SSO方案