مقدمة
\nتطوير الويب الحديث لا يمكن تصوره بدون مصادقة وتفويض آمنين للمستخدمين. المعياران الأكثر شيوعًا — OAuth 2.0 و JWT (JSON Web Tokens) — أصبحا الحلين الصناعيين الفعليين. إذا كنت تكتب بلغة Rust، فأنت محظوظ: هذه اللغة لا تقدم فقط أعلى أداء، بل أيضًا مكتبات قوية لتنفيذ هذه البروتوكولات.
\nفي هذه المقالة، سنشرح ما هما OAuth 2.0 و JWT، وكيف يعملان معًا، وسنكتب مثالًا بسيطًا لكن عمليًا للتفويض في Rust باستخدام إطار العمل actix-web والكريد jsonwebtoken. المقالة موجهة للمبتدئين، لذا سننتقل خطوة بخطوة — من النظرية إلى الكود.
ما هما OAuth 2.0 و JWT؟
\nقبل كتابة الكود، من المهم فهم المفاهيم. OAuth 2.0 هو بروتوكول تفويض يسمح للتطبيقات بالحصول على وصول محدود إلى موارد المستخدم دون نقل كلمة المرور الخاصة به. يُستخدم عندما تسجل الدخول إلى موقع عبر Google أو GitHub أو VK.
\nJWT هو تنسيق رمز مميز (JSON Object Signing and Encryption) يحتوي على ادعاءات (claims) حول المستخدم. يتم توقيع الرمز المميز بواسطة الخادم، لذلك لا يمكن للعميل تزويره. في سياق OAuth 2.0، غالبًا ما يُستخدم JWT كرمز وصول (access token).
\nالسيناريو النموذجي: يقوم المستخدم بإدخال اسم المستخدم وكلمة المرور، يتحقق الخادم منهما، ويُنشئ رمز JWT ويعيده إلى العميل. يرسل العميل مع كل طلب هذا الرمز المميز في الترويسة Authorization: Bearer <token>. يتحقق الخادم من التوقيع ويستخرج بيانات المستخدم.
إعداد المشروع في Rust
\nلننشئ مشروعًا جديدًا:
\ncargo new rust_oauth_jwt\ncd rust_oauth_jwt\nلنضف التبعيات الضرورية في Cargo.toml:
[dependencies]\nactix-web = "4"\nactix-rt = "2"\nserde = { version = "1", features = ["derive"] }\nserde_json = "1"\njsonwebtoken = "9"\nchrono = { version = "0.4", features = ["serde"] }\ndotenv = "0.15"\nenv_logger = "0.11"\nlog = "0.4"\nلننشئ ملف .env في جذر المشروع لتخزين المفتاح السري:
JWT_SECRET=my_super_secret_key_change_in_production\nJWT_EXPIRATION_HOURS=24\nلنقم بتحميل متغيرات البيئة في main.rs:
use dotenv::dotenv;\nuse std::env;\n\nfn main() {\n dotenv().ok();\n env_logger::init();\n // ... باقي الكود\n}\n\nإنشاء هيكل المستخدم ورمز JWT
\nأولاً، لنحدد نموذج المستخدم وهيكل الادعاءات لـ JWT. لننشئ ملف models.rs:
use serde::{Deserialize, Serialize};\n\n#[derive(Debug, Serialize, Deserialize, Clone)]\npub struct User {\n pub id: i32,\n pub username: String,\n pub password_hash: String, // في المشروع الحقيقي، خزّن الهاش، وليس كلمة المرور!\n}\n\n#[derive(Debug, Serialize, Deserialize)]\npub struct Claims {\n pub sub: String, // الموضوع (معرف المستخدم)\n pub username: String,\n pub exp: usize, // طابع زمني لانتهاء الصلاحية\n}\nالآن، لننشئ أداة مساعدة للعمل مع JWT في jwt_utils.rs:
use chrono::{Duration, Utc};\nuse jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};\nuse crate::models::Claims;\n\npub fn create_jwt(user_id: &str, username: &str) -> Result<String, jsonwebtoken::errors::Error> {\n let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");\n let expiration_hours: i64 = std::env::var("JWT_EXPIRATION_HOURS")\n .unwrap_or_else(|_| "24".to_string())\n .parse()\n .unwrap();\n\n let expiration = Utc::now()\n .checked_add_signed(Duration::hours(expiration_hours))\n .expect("valid timestamp")\n .timestamp() as usize;\n\n let claims = Claims {\n sub: user_id.to_owned(),\n username: username.to_owned(),\n