OAuth 2.0 وتفويض JWT في Rust: دليل خطوة بخطوة للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة

\n

تطوير الويب الحديث لا يمكن تصوره بدون مصادقة وتفويض آمنين للمستخدمين. المعياران الأكثر شيوعًا — OAuth 2.0 و JWT (JSON Web Tokens) — أصبحا الحلين الصناعيين الفعليين. إذا كنت تكتب بلغة Rust، فأنت محظوظ: هذه اللغة لا تقدم فقط أعلى أداء، بل أيضًا مكتبات قوية لتنفيذ هذه البروتوكولات.

\n

في هذه المقالة، سنشرح ما هما OAuth 2.0 و JWT، وكيف يعملان معًا، وسنكتب مثالًا بسيطًا لكن عمليًا للتفويض في Rust باستخدام إطار العمل actix-web والكريد jsonwebtoken. المقالة موجهة للمبتدئين، لذا سننتقل خطوة بخطوة — من النظرية إلى الكود.

\n\n

ما هما OAuth 2.0 و JWT؟

\n

قبل كتابة الكود، من المهم فهم المفاهيم. OAuth 2.0 هو بروتوكول تفويض يسمح للتطبيقات بالحصول على وصول محدود إلى موارد المستخدم دون نقل كلمة المرور الخاصة به. يُستخدم عندما تسجل الدخول إلى موقع عبر Google أو GitHub أو VK.

\n

JWT هو تنسيق رمز مميز (JSON Object Signing and Encryption) يحتوي على ادعاءات (claims) حول المستخدم. يتم توقيع الرمز المميز بواسطة الخادم، لذلك لا يمكن للعميل تزويره. في سياق OAuth 2.0، غالبًا ما يُستخدم JWT كرمز وصول (access token).

\n

السيناريو النموذجي: يقوم المستخدم بإدخال اسم المستخدم وكلمة المرور، يتحقق الخادم منهما، ويُنشئ رمز JWT ويعيده إلى العميل. يرسل العميل مع كل طلب هذا الرمز المميز في الترويسة Authorization: Bearer <token>. يتحقق الخادم من التوقيع ويستخرج بيانات المستخدم.

\n\n

إعداد المشروع في Rust

\n

لننشئ مشروعًا جديدًا:

\n
cargo new rust_oauth_jwt\ncd rust_oauth_jwt
\n

لنضف التبعيات الضرورية في Cargo.toml:

\n
[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 في جذر المشروع لتخزين المفتاح السري:

\n
JWT_SECRET=my_super_secret_key_change_in_production\nJWT_EXPIRATION_HOURS=24
\n

لنقم بتحميل متغيرات البيئة في main.rs:

\n
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:

\n
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:

\n
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                

المدونات

توصيات الكتب