第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:分布式验证
优点:
无单点故障
扩展性好
缺点:
各服务需要验证逻辑
密钥管理复杂
适用:
大规模
高可用要求