Go: برمجيات الوسيطة Go ومصادقة JWT
آخر تحديث: 2026-08-26
البرمجيات الوسيطة هي «مسار» الخدمات الصغيرة في لغة Go — المصادقة، وتحديد معدل الاستخدام، والتسجيل، والاستعادة؛ فكل مسألة شاملة تعد جزءًا من البرمجيات الوسيطة.
عندما تحتاج إلى التحقق من مصادقة JWT، أو تسجيل الأنشطة، أو فرض حدود المعدل في كل نقطة نهاية لواجهة برمجة التطبيقات (API)، فإن نمط «البرمجيات الوسيطة» يتيح لك كتابة الكود مرة واحدة وتطبيقه في كل مكان.
1. ستتعلم
- إنشاء توقيع JWT والتحقق من صحته
- رمز الوصول + رمز التحديث (رمزان)
- RBAC (التحكم في الوصول القائم على الأدوار)
- التحكم في التدفق باستخدام نموذج «دلو الرموز»
- الاستعادة / التسجيل / برمجيات الوسيطة CORS
- مسارات عمل البرمجيات الوسيطة المتتالية
2. قصة حقيقية لمهندس برمجيات الخلفية
(1) المشكلة: التحقق يدويًّا من الرمز المميز في كل جهاز توجيه؛ فقد نسيت القيام بذلك ثلاث مرات.
يعمل تشارلي ضمن فريق API Gateway، وهو مسؤول عن المصادقة لجميع نقاط النهاية:
"طلب المدير أن تقوم جميع واجهات برمجة التطبيقات (API) بالتحقق من صحة رموز JWT. فقمت بنسخ ولصق 15 سطراً من كود التحقق من صحة الرموز في كل معالج. وفي اليوم الثاني بعد النشر، نسيت إضافة ميزة المصادقة إلى
/admin/users— مما سمح لأي شخص بحذف المستخدمين مباشرةً. فقال المدير التقني: «كشفت عملية التدقيق الأمني عن ثغرة أمنية خطيرة.»"
// Bad code: manually check in every handler
func deleteUser(w http.ResponseWriter, r *http.Request) {
// Forgot to add Token verification! Serious security vulnerability
userID := r.PathValue("id")
deleteUserFromDB(userID)
}
func updateOrder(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
// Copy 15 lines of verification code each time
if !validateToken(token) {
http.Error(w, "unauthorized", 401)
return
}
// Business logic...
}
(2) حل Go: برمجيات وسيطة للمصادقة
// Good code: auth middleware, implement once, apply everywhere
func AuthMiddleware(jwtSecret string) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := extractToken(r)
claims, err := validateJWT(token, jwtSecret)
if err != nil {
writeError(w, http.StatusUnauthorized, "invalid token")
return
}
// Inject user info into Context
ctx := context.WithValue(r.Context(), "user", claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// Registration only needs one Middleware
mux.Handle("POST /api/orders", AuthMiddleware(secret)(orderHandler))
(3) المزايا: الطريقة اليدوية مقابل البرمجيات الوسيطة
| البعد | الفحص اليدوي | نمط البرمجيات الوسيطة |
|---|---|---|
| خطر الإغفال | هناك خطر نسيان إضافة كل مسار جديد | صفر |
| تكرار الكود | 15 سطراً لكل معالج | 0 سطر (تركز المعالجات حصريًّا على المنطق التجاري) |
| تعديل المنطق | تعديل 20 معالجًا | تعديل برنامج وسيط واحد |
| اختبار الوحدة | اختبار المصادقة لكل معالج | اختبار البرمجيات الوسيطة مرة واحدة فقط |
3. المصادقة باستخدام JWT
▶ مثال: إنشاء JWT والتحقق من صحته
⚙️ المتطلبات الأساسية: في بيئة الإنتاج، استخدم
go get github.com/golang-jwt/jwt/v5. الشفرة أدناه هي تطبيق يدوي لأغراض تعليمية فقط.
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
)
// ---------- JWT manual impl (no third-party dependency) ----------
type JWTClaims struct {
UserID int `json:"user_id"`
Role string `json:"role"`
Permissions []string `json:"permissions"`
ExpiresAt int64 `json:"exp"`
}
type JWTHeader struct {
Alg string `json:"alg"`
Typ string `json:"typ"`
}
func base64Encode(data []byte) string {
return strings.TrimRight(base64.URLEncoding.EncodeToString(data), "=")
}
func base64Decode(s string) ([]byte, error) {
// Pad to proper length
switch len(s) % 4 {
case 2:
s += "=="
case 3:
s += "="
}
return base64.URLEncoding.DecodeString(s)
}
func createJWT(claims JWTClaims, secret string) (string, error) {
header := JWTHeader{Alg: "HS256", Typ: "JWT"}
headerJSON, _ := json.Marshal(header)
claimsJSON, _ := json.Marshal(claims)
headerEnc := base64Encode(headerJSON)
claimsEnc := base64Encode(claimsJSON)
// Sign
message := headerEnc + "." + claimsEnc
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(message))
signature := base64Encode(mac.Sum(nil))
return message + "." + signature, nil
}
func validateJWT(token string, secret string) (*JWTClaims, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("invalid token format")
}
// Verify signature
message := parts[0] + "." + parts[1]
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(message))
expectedSig := base64Encode(mac.Sum(nil))
if !hmac.Equal([]byte(parts[2]), []byte(expectedSig)) {
return nil, fmt.Errorf("invalid signature")
}
// Parse claims
claimsJSON, err := base64Decode(parts[1])
if err != nil {
return nil, err
}
var claims JWTClaims
if err := json.Unmarshal(claimsJSON, &claims); err != nil {
return nil, err
}
// Check expiration
if time.Now().Unix() > claims.ExpiresAt {
return nil, fmt.Errorf("token expired")
}
return &claims, nil
}
func main() {
secret := "my-secret-key"
// Generate Token
claims := JWTClaims{
UserID: 1,
Role: "admin",
ExpiresAt: time.Now().Add(1 * time.Hour).Unix(),
}
token, _ := createJWT(claims, secret)
fmt.Printf("JWT: %s\n", token)
// Validate Token
validated, err := validateJWT(token, secret)
if err != nil {
fmt.Printf("Validation failed: %v\n", err)
} else {
fmt.Printf("Validation succeeded: user=%d, role=%s\n", validated.UserID, validated.Role)
}
}
github.com/golang-jwt/jwt/v5) بدلاً من تنفيذها يدويًّا. يهدف الكود أعلاه حصريًّا إلى توضيح كيفية عمل JWT. تتعامل مكتبات الجهات الخارجية مع المزيد من الحالات الاستثنائية (مثل أنواع الرموز المميزة، وتناوب المفاتيح، والتحقق من المطالبات القياسية).
4. رمز الوصول + رمز التحديث
▶ مثال: المصادقة باستخدام رمزين
package main
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"sync"
"time"
)
// ---------- Token Service ----------
type TokenService struct {
secret string
refreshTTL time.Duration
// Store refresh tokens (use Redis in production)
refreshTokens map[string]int // token → userID
mu sync.RWMutex
}
func NewTokenService(secret string) *TokenService {
return &TokenService{
secret: secret,
refreshTTL: 7 * 24 * time.Hour,
refreshTokens: make(map[string]int),
}
}
func (s *TokenService) GenerateTokens(userID int, role string) (accessToken, refreshToken string, err error) {
// Access Token (expires in 15 minutes)
accessClaims := JWTClaims{
UserID: userID,
Role: role,
ExpiresAt: time.Now().Add(15 * time.Minute).Unix(),
}
accessToken, err = createJWT(accessClaims, s.secret)
if err != nil {
return "", "", err
}
// Refresh Token (random string, expires in 7 days)
bytes := make([]byte, 32)
rand.Read(bytes)
refreshToken = hex.EncodeToString(bytes)
s.mu.Lock()
s.refreshTokens[refreshToken] = userID
s.mu.Unlock()
return accessToken, refreshToken, nil
}
func (s *TokenService) RefreshAccessToken(refreshToken string) (string, error) {
s.mu.RLock()
userID, exists := s.refreshTokens[refreshToken]
s.mu.RUnlock()
if !exists {
return "", fmt.Errorf("invalid refresh token")
}
// Generate new Access Token
claims := JWTClaims{
UserID: userID,
Role: "user",
ExpiresAt: time.Now().Add(15 * time.Minute).Unix(),
}
return createJWT(claims, s.secret)
}
func (s *TokenService) RevokeRefreshToken(refreshToken string) {
s.mu.Lock()
delete(s.refreshTokens, refreshToken)
s.mu.Unlock()
}
// ---------- Auth Handler ----------
type AuthHandler struct {
tokenSvc *TokenService
}
type loginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type tokenResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
TokenType string `json:"token_type"`
ExpiresIn int64 `json:"expires_in"`
}
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
var req loginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
return
}
// Verify username and password (use bcrypt in production)
if req.Username != "admin" || req.Password != "password" {
writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid credentials"})
return
}
accessToken, refreshToken, err := h.tokenSvc.GenerateTokens(1, "admin")
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "token generation failed"})
return
}
writeJSON(w, http.StatusOK, tokenResponse{
AccessToken: accessToken,
RefreshToken: refreshToken,
TokenType: "Bearer",
ExpiresIn: 900, // 15 minutes
})
}
func (h *AuthHandler) Refresh(w http.ResponseWriter, r *http.Request) {
var req struct {
RefreshToken string `json:"refresh_token"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
return
}
accessToken, err := h.tokenSvc.RefreshAccessToken(req.RefreshToken)
if err != nil {
writeJSON(w, http.StatusUnauthorized, map[string]string{"error": "invalid refresh token"})
return
}
writeJSON(w, http.StatusOK, map[string]string{"access_token": accessToken})
}
func main() {
tokenSvc := NewTokenService("my-secret-key")
authHandler := &AuthHandler{tokenSvc: tokenSvc}
mux := http.NewServeMux()
mux.HandleFunc("POST /login", authHandler.Login)
mux.HandleFunc("POST /refresh", authHandler.Refresh)
fmt.Println("Auth service listening on :8080")
http.ListenAndServe(":8080", mux)
}
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
sequenceDiagram
participant Client
participant API as API Gateway
participant Auth as Auth Service
Client->>API: POST /login (username, password)
API->>Auth: Verify credentials
Auth-->>API: access_token (15min) + refresh_token (7d)
API-->>Client: Return dual tokens
Client->>API: GET /orders (Bearer access_token)
API->>API: Validate access_token
API-->>Client: 200 OK
Client->>API: GET /orders (access_token expired)
API-->>Client: 401 Unauthorized
Client->>API: POST /refresh (refresh_token)
API->>Auth: Validate refresh_token
Auth-->>API: New access_token
API-->>Client: Return new access_token
5. البرمجيات الوسيطة الخاصة بأذونات RBAC
▶ مثال: تطبيق نموذج RBAC
package main
import (
"context"
"encoding/json"
"net/http"
)
type UserRole string
const (
RoleAdmin UserRole = "admin"
RoleUser UserRole = "user"
RoleGuest UserRole = "guest"
)
// Permission definitions
var rolePermissions = map[UserRole][]string{
RoleAdmin: {"read:users", "write:users", "حذف:users", "read:orders", "write:orders"},
RoleUser: {"read:orders", "write:orders"},
RoleGuest: {"read:products"},
}
type Claims struct {
UserID int
Role UserRole
}
// Get claims from Context
func GetClaims(r *http.Request) *Claims {
claims, _ := r.Context().Value("claims").(*Claims)
return claims
}
// RBAC middleware
func RequirePermission(permission string) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims := GetClaims(r)
if claims == nil {
writeError(w, http.StatusUnauthorized, "not authenticated")
return
}
permissions, exists := rolePermissions[claims.Role]
if !exists {
writeError(w, http.StatusForbidden, "no permissions defined for role")
return
}
// Check permission
hasPermission := false
for _, p := range permissions {
if p == permission {
hasPermission = true
break
}
}
if !hasPermission {
writeError(w, http.StatusForbidden, "insufficient permissions")
return
}
next.ServeHTTP(w, r)
})
}
}
// ---------- Route registration ----------
func setupRoutes() http.Handler {
mux := http.NewServeMux()
// Public endpoints
mux.HandleFunc("POST /login", loginHandler)
// Authenticated endpoints
mux.Handle("GET /api/orders", RequirePermission("read:orders")(authMiddleware(http.HandlerFunc(listOrders))))
mux.Handle("POST /api/orders", RequirePermission("write:orders")(authMiddleware(http.HandlerFunc(createOrder))))
mux.Handle("DELETE /api/users/{id}", RequirePermission("حذف:users")(authMiddleware(http.HandlerFunc(deleteUser))))
return mux
}
(2) مسار البرمجيات الوسيطة
Request → Auth Middleware → RBAC Middleware → Handler
| |
Verify JWT Token Check Role permissions
Inject Claims User has permission?
| المستوى | البرمجيات الوسيطة | المسؤوليات |
|---|---|---|
| 1 | الاسترداد | Catch panic، return 500 |
| 2 | التسجيل | مدة طلب السجل |
| 3 | CORS | معالجة الطلبات عبر المصادر |
| 4 | المصادقة | التحقق من JWT، وإدراج معلومات المستخدم |
| 5 | RBAC | التحقق من أذونات الدور |
| 6 | الحد الأقصى للمعدل | التحكم في معدل الاستخدام باستخدام «Token Bucket» |
| 7 | المعالج | منطق الأعمال |
6. التحكم في التدفق باستخدام نموذج «دلو الرموز»
▶ مثال: تطبيق نموذج «دلو الرموز»
package main
import (
"net/http"
"sync"
"time"
)
type TokenBucket struct {
mu sync.Mutex
tokens float64
maxTokens float64
refillRate float64
lastRefill time.Time
}
func NewTokenBucket(rate float64, burst int) *TokenBucket {
return &TokenBucket{
tokens: float64(burst),
maxTokens: float64(burst),
refillRate: rate,
lastRefill: time.Now(),
}
}
func (tb *TokenBucket) Allow() bool {
tb.mu.Lock()
defer tb.mu.Unlock()
// Refill tokens
now := time.Now()
elapsed := now.Sub(tb.lastRefill).Seconds()
tb.tokens = min(tb.tokens+elapsed*tb.refillRate, tb.maxTokens)
tb.lastRefill = now
// Take a token
if tb.tokens >= 1 {
tb.tokens--
return true
}
return false
}
func min(a, b float64) float64 {
if a < b {
return a
}
return b
}
// IP rate limiter
type IPRateLimiter struct {
mu sync.RWMutex
buckets map[string]*TokenBucket
rate float64
burst int
}
func NewIPRateLimiter(rate float64, burst int) *IPRateLimiter {
return &IPRateLimiter{
buckets: make(map[string]*TokenBucket),
rate: rate,
burst: burst,
}
}
func (rl *IPRateLimiter) GetBucket(ip string) *TokenBucket {
rl.mu.Lock()
defer rl.mu.Unlock()
bucket, exists := rl.buckets[ip]
if !exists {
bucket = NewTokenBucket(rl.rate, rl.burst)
rl.buckets[ip] = bucket
}
return bucket
}
// RateLimiterMiddleware
func RateLimiterMiddleware(rate float64, burst int) Middleware {
limiter := NewIPRateLimiter(rate, burst)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
bucket := limiter.GetBucket(ip)
if !bucket.Allow() {
w.Header().Set("Retry-After", "1")
writeError(w, http.StatusTooManyRequests, "rate limit exceeded")
return
}
next.ServeHTTP(w, r)
})
}
}
7. مثال كامل: مسار عمل البرمجيات الوسيطة في API Gateway
▶ مثال: مسار العمل الكامل
// api_gateway.go
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"os/signal"
"strings"
"syscall"
"time"
)
// ---------- Type definitions ----------
type Middleware func(http.Handler) http.Handler
type Claims struct {
UserID int
Role string
}
// ---------- Middleware ----------
// Recovery
func Recovery(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("[PANIC] %v", err)
writeError(w, http.StatusInternalServerError, "internal error")
}
}()
next.ServeHTTP(w, r)
})
}
// Logging
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("[%s] %s %s", r.Method, r.URL.Path, r.RemoteAddr)
next.ServeHTTP(w, r)
log.Printf("[%s] %s → %v", r.Method, r.URL.Path, time.Since(start))
})
}
// CORS
func CORS(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
// Auth (JWT authentication)
func Auth(secret string) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if !strings.HasPrefix(authHeader, "Bearer ") {
writeError(w, http.StatusUnauthorized, "missing token")
return
}
token := authHeader[7:]
claims, err := validateJWT(token, secret)
if err != nil {
writeError(w, http.StatusUnauthorized, err.Error())
return
}
ctx := context.WithValue(r.Context(), "claims", &Claims{
UserID: claims.UserID,
Role: claims.Role,
})
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// RBAC
func RequireRole(roles ...string) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims, ok := r.Context().Value("claims").(*Claims)
if !ok {
writeError(w, http.StatusUnauthorized, "not authenticated")
return
}
for _, role := range roles {
if claims.Role == role {
next.ServeHTTP(w, r)
return
}
}
writeError(w, http.StatusForbidden, "insufficient permissions")
})
}
}
// Rate Limiter
var rateLimiter = NewIPRateLimiter(10, 20) // 10 req/s, burst 20
func RateLimit(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip := r.RemoteAddr
bucket := rateLimiter.GetBucket(ip)
if !bucket.Allow() {
writeError(w, http.StatusTooManyRequests, "rate limit exceeded")
return
}
next.ServeHTTP(w, r)
})
}
// ---------- Chain ----------
func Chain(h http.Handler, mws ...Middleware) http.Handler {
for i := len(mws) - 1; i >= 0; i-- {
h = mws[i](h)
}
return h
}
// ---------- Handlers ----------
type OrderHandler struct{}
func (h *OrderHandler) ListOrders(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"orders": "list"})
}
func (h *OrderHandler) CreateOrder(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusCreated, map[string]string{"الترتيب": "created"})
}
type AdminHandler struct{}
func (h *AdminHandler) DeleteUser(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"deleted": "user"})
}
// ---------- Utility functions ----------
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
func writeError(w http.ResponseWriter, status int, msg string) {
writeJSON(w, status, map[string]string{"error": msg})
}
// ---------- Main ----------
func main() {
jwtSecret := "super-secret-key"
orders := &OrderHandler{}
mux := http.NewServeMux()
// Public endpoint
mux.HandleFunc("POST /login", func(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"token": "login-token"})
})
// Authenticated + rate-limited endpoints
mux.Handle("GET /api/orders", Chain(
http.HandlerFunc(orders.ListOrders),
Auth(jwtSecret),
RateLimit,
))
mux.Handle("POST /api/orders", Chain(
http.HandlerFunc(orders.CreateOrder),
Auth(jwtSecret),
RateLimit,
))
// Admin endpoints (Auth + RBAC + RateLimit)
mux.Handle("DELETE /api/users/{id}", Chain(
http.HandlerFunc((&AdminHandler{}).DeleteUser),
Auth(jwtSecret),
RequireRole("admin"),
RateLimit,
))
// Global middleware (Recovery → Logging → CORS → routes)
app := Chain(mux, Recovery, Logging, CORS)
server := &http.Server{Addr: ":8080", Handler: app}
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
log.Println("Shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
server.Shutdown(ctx)
}()
log.Println("API Gateway listening on :8080")
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatal(err)
}
}
❓ أسئلة شائعة
github.com/golang-jwt/jwt/v5. تتميز رموز الوصول بفترة صلاحية قصيرة (15 دقيقة)، في حين تتميز رموز التحديث بفترة صلاحية طويلة (7 أيام) ويتم تخزينها على الخادم.rolePermissions[role] = []permission + تقوم البرمجيات الوسيطة بالتحقق permission in rolePermissions[claims.Role].debug.Stack() بطباعة تتبع المكدس. استخدم مستوى السجل ERROR. في بيئة الإنتاج، لا تقم بإرجاع تتبع المكدس إلى العميل — قم بتسجيله على الخادم فقط.Chain(h, A, B, C) → يمر الطلب عبر A → B → C → Handler → C → B → A. ترتيب التسجيل = من الطبقة الخارجية إلى الطبقة الداخلية. يقع Recovery في الطبقة الخارجية (يلتقط حالات الذعر التي تطلقها أي طبقة)، ويقع Handler في الطبقة الداخلية (منطق الأعمال).📖 ملخص
- JWT: الرأس + الحمولة + التوقيع،
golang-jwt/jwt/v5 - رمزان: الدخول (15 دقيقة) + التجديد (7 أيام)
- برنامج وسيط المصادقة: يتحقق من صحة الرمز المميز ويقوم بإدراج المطالبات في السياق
- RBAC: ربط الأدوار → الأذونات؛ تقوم البرمجيات الوسيطة بالتحقق من الأذونات
- التحكم في التدفق باستخدام نموذج «دلو الرموز» (Token Bucket): يسمح بحدوث ارتفاعات مفاجئة في التدفق، ويتحكم في متوسط التدفق
- البرمجيات الوسيطة للاستعادة: تلتقط حالة «panic» وتُرجع رمز الخطأ 500
- ترتيب البرامج الوسيطة: الاسترداد → التسجيل → CORS → المصادقة → RBAC → الحد الأقصى للمعدل → المعالج
📝 تمارين
-
أساسي (مستوى الصعوبة ⭐): قم بتنفيذ برنامج وسيط بسيط للمصادقة. أنشئ نقطة نهاية
/api/protectedتتطلب أن يتضمن رأس الطلبAuthorization: Bearer token123. إذا كان الرمز الصالح صالحًا، فقم بإرجاع{"message": "access granted"}؛ وإلا، فقم بإرجاع خطأ 401. -
متقدم (درجة الصعوبة ⭐⭐): تنفيذ نظام مصادقة JWT كامل. المتطلبات: (1)
POST /registerللتسجيل (تشفير كلمة المرور باستخدام bcrypt)؛ (2)POST /loginلتسجيل الدخول (يعيد access_token + refresh_token)؛ (3) برنامج وسيط للتوثيق (Auth middleware) يتحقق من صحة access_token لجميع نقاط النهاية/api/*؛ (4)POST /refreshلاستبدالrefresh_tokenبـaccess_tokenجديد؛ (5)POST /logoutلإلغاءrefresh_token. -
التحدي (الصعوبة ⭐⭐⭐): تنفيذ واجهة برمجة تطبيقات (API) لإدارة المدونة مع تطبيق نموذج RBAC والحد من معدل الاستخدام. المتطلبات: (1) ثلاثة أدوار: المسؤول، والمحرر، والقارئ؛ (2) المسؤول: صلاحيات كاملة؛ (3) المحرر: إنشاء وتحرير وحذف منشوراته الخاصة؛ (4) القارئ: وصول للقراءة فقط؛ (5) لكل دور حدود معدل مختلفة؛ (6) يجب أن تمر جميع نقاط النهاية عبر مسار البرمجيات الوسيطة التالي: الاسترداد → التسجيل → المصادقة → RBAC → RateLimit → المعالج؛ (7) استخدم
-raceللتحقق من أمان التزامن.