التفويض والمصادقة في Go (Golang)
في تطوير الويب الحديث باستخدام Go (Golang)، تُعد أمان بيانات المستخدم وتحديد صلاحيات الوصول من الجوانب الحيوية للغاية. تسمح المكتبات والأدوات الخاصة بالتفويض والمصادقة للمطورين بتنفيذ الحماية في تطبيقاتهم بسرعة وموثوقية. في هذه المقالة، سنستعرض الأساليب الأساسية والمكتبات الشائعة لتنفيذ المصادقة (التحقق من هوية المستخدم) والتفويض (تحديد صلاحيات الوصول) في Go.
المفاهيم الأساسية
قبل الانتقال إلى الأدوات، من المهم التمييز بين مفهومين رئيسيين:
- المصادقة — عملية التحقق من أن المستخدم هو من يدّعي (عادةً من خلال اسم المستخدم/كلمة المرور، الرموز المميزة، OAuth).
- التفويض — عملية تحديد الموارد والإجراءات المتاحة للمستخدم المُوثّق (الأدوار، صلاحيات الوصول).
المكتبات والأدوات الشائعة
1. bcrypt لتجزئة كلمات المرور
مكتبة golang.org/x/crypto/bcrypt هي المعيار الفعلي للتخزين الآمن لكلمات المرور. تقوم تلقائيًا بإضافة الملح (salt) وتستخدم دالة تجزئة تكيفية.
package main
import ( "fmt" "golang.org/x/crypto/bcrypt")
func main() { password := "mySecretPassword123" // تجزئة كلمة المرور hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { panic(err) } fmt.Printf("Hash: %s", hash) // التحقق من كلمة المرور err = bcrypt.CompareHashAndPassword(hash, []byte("wrongPassword")) if err != nil { fmt.Println("كلمة المرور غير صحيحة:", err) } else { fmt.Println("كلمة المرور صحيحة") } // تحقق ناجح err = bcrypt.CompareHashAndPassword(hash, []byte(password)) if err == nil { fmt.Println("كلمة المرور صحيحة!") }}2. JWT (JSON Web Tokens) مع مكتبة golang-jwt
مكتبة github.com/golang-jwt/jwt/v5 تسمح بإنشاء والتحقق من رموز JWT. هذه طريقة شائعة لتنفيذ المصادقة عديمة الحالة (stateless).
package main
import ( "fmt" "time" "github.com/golang-jwt/jwt/v5")
var jwtSecret = []byte("super-secret-key")
func createToken(userID string) (string, error) { claims := jwt.MapClaims{ "user_id": userID, "exp": time.Now().Add(time.Hour * 24).Unix(), "iat": time.Now().Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(jwtSecret)}
func validateToken(tokenString string) (string, error) { token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("طريقة توقيع غير متوقعة: %v", token.Header["alg"]) } return jwtSecret, nil }) if err != nil { return "", err } if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { return claims["user_id"].(string), nil } return "", fmt.Errorf("رمز غير صالح")}
func main() { // إنشاء رمز token, _ := createToken("user123") fmt.Println("JWT Token:", token) // التحقق من الرمز userID, err := validateToken(token) if err != nil { fmt.Println("خطأ في التحقق:", err) } else { fmt.Println("معرف المستخدم من الرمز:", userID) }}3. المصادقة القائمة على الجلسات مع gorilla/sessions
للمصادقة التقليدية القائمة على الجلسات، استخدم github.com/gorilla/sessions. يتم تخزين الجلسات على الخادم، ويتم إرسال ملف تعريف ارتباط (cookie) يحتوي على معرف الجلسة فقط إلى العميل.
package main
import ( "net/http" "github.com/gorilla/sessions")\