OAuth 2.0 وتفويض JWT بلغة Kotlin: للمبتدئين
تطبيقات الويب وواجهات API الحديثة نادرًا ما تستغني عن نظام المصادقة والتفويض. المعياران الأكثر شيوعًا في هذا المجال هما OAuth 2.0 و JWT (JSON Web Token). في هذه المقالة، سنشرح كيفية تنفيذ التفويض بلغة Kotlin باستخدام هذه التقنيات. المادة موجهة للمطورين المبتدئين الذين لديهم معرفة بأساسيات اللغة وإطار العمل Spring Boot.
ما هما OAuth 2.0 و JWT؟
قبل كتابة الكود، من المهم فهم النظرية.
OAuth 2.0 هو بروتوكول تفويض يسمح للتطبيقات بالحصول على وصول محدود إلى موارد المستخدم دون نقل اسم المستخدم وكلمة المرور. تخيل أنك تسجل الدخول إلى موقع عبر "تسجيل الدخول عبر Google" - هذا هو OAuth 2.0.
JWT هو تنسيق رمز مميز مضغوط ومكتفي بذاته. يتكون من ثلاثة أجزاء: الرأس (header)، والحمولة (payload)، والتوقيع (signature). غالبًا ما يُستخدم JWT مع OAuth 2.0 لنقل معلومات المصادقة.
التدفقات الرئيسية لـ OAuth 2.0:
- Authorization Code Flow - لتطبيقات الخادم (الأكثر أمانًا)
- Client Credentials Flow - للاتصال بين الخادم والخادم
- Implicit Flow - قديم، غير موصى به
- Resource Owner Password Flow - فقط إذا كنت تثق بالعميل
في هذه المقالة، سنركز على Client Credentials Flow، لأنه الأسهل للفهم وغالبًا ما يُستخدم في الخدمات المصغرة.
إعداد مشروع Spring Boot بلغة Kotlin
لننشئ مشروع Spring Boot جديدًا مع دعم Kotlin. سنضيف التبعيات اللازمة في ملف build.gradle.kts:
dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server") implementation("io.jsonwebtoken:jjwt-api:0.11.5") runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5") runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5") implementation("com.fasterxml.jackson.module:jackson-module-kotlin")}توفر هذه التبعيات دعم Spring Security و OAuth 2.0 Resource Server ومكتبة JJWT للعمل مع JWT.
إنشاء رموز JWT بلغة Kotlin
لنكتب خدمة لتوليد والتحقق من صحة JWT. هذا هو أساس التفويض لدينا.
import io.jsonwebtoken.Jwtsimport io.jsonwebtoken.SignatureAlgorithmimport io.jsonwebtoken.security.Keysimport org.springframework.stereotype.Serviceimport java.util.Date
@Serviceclass JwtService { private val secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256) private val expirationMs = 3600000L // 1 ساعة
fun generateToken(clientId: String): String { val now = Date() val expiryDate = Date(now.time + expirationMs)
return Jwts.builder() .setSubject(clientId) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(secretKey) .compact() }
fun validateToken(token: String): Boolean { return try { Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) true } catch (e: Exception) { false } }
fun getClientIdFromToken(token: String): String { val claims = Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(token) .body return claims.subject }}في هذه الخدمة، نستخدم مفتاحًا متماثلًا. في بيئة الإنتاج، من الأفضل استخدام تشفير غير متماثل (RS256) و