أساسيات Spring Security

مقدمة إلى Spring Security

18 دقيقة الدرس 1 من 13

مقدمة إلى Spring Security

يواجه كل تطبيق ويب في نهاية المطاف السؤال ذاته: من يُسمح له بالدخول، وما الذي يُتاح له فعله؟ Spring Security هو الإجابة المعيارية في نظام Spring البيئي. إنه إطار عمل قوي وقابل للتخصيص بدرجة عالية، يتولى معالجة المصادقة (التحقق من هويتك) والتفويض (تحديد ما يمكنك الوصول إليه)، فضلًا عن توفير حماية من سلسلة واسعة من هجمات طبقة الويب الشائعة.

يتناول هذا الدرس ما يحميك منه Spring Security فعلًا، والنموذج الذهني الذي تحتاجه قبل كتابة أي إعدادات، وكيف يندمج هذا المكتبة مع تطبيق Spring Boot 3 دون الحاجة إلى كود توصيل إضافي.

ما التهديدات التي يعالجها Spring Security؟

قبل أي إعداد، من المفيد أن نكون دقيقين بشأن الهجمات التي يحميك منها Spring Security افتراضيًا.

  • تزوير الطلبات عبر المواقع (CSRF): تخدع صفحة ضارة متصفح المستخدم لإطلاق طلب يُغيّر الحالة (مثل تحويل مصرفي) إلى تطبيقك متنكرًا في هيئة كوكي الجلسة الخاصة بالمستخدم. يُفعّل Spring Security حماية رمز CSRF تلقائيًا لكل أسلوب HTTP يُحدث تغييرًا في الحالة (POST وPUT وDELETE وPATCH).
  • تثبيت الجلسة: يفرض المهاجم معرّف جلسة معروفًا على الضحية قبل تسجيل الدخول. يُهاجر Spring Security الجلسة (أو يُبطلها) عند كل مصادقة ناجحة، مما يجعل معرّفات الجلسات الصادرة مسبقًا عديمة الفائدة.
  • الضغط الخفي على النقر (Clickjacking): يتيح تضمين موقعك في إطار iframe مخفي للمهاجم تراكب واجهة مستخدم مزيفة فوقه. يُرسل Spring Security رأس X-Frame-Options: DENY افتراضيًا.
  • استنشاق المحتوى وناقلات XSS: رؤوس مثل X-Content-Type-Options ودعم Content-Security-Policy المدمج في HeadersConfigurer تُقلّل من الهجمات على مستوى المتصفح.
  • تخمين بيانات الاعتماد بالقوة الغاشمة: بفرض المصادقة على كل مسار حساس والتكامل مع استراتيجيات تشفير كلمات المرور، يُزيل Spring Security الفرص السهلة أمام هجمات حشو بيانات الاعتماد الآلية.
موقف آمن افتراضيًا: إذا أضفت Spring Security إلى مشروع دون أي إعداد إضافي، فإنه يقفل كل عنوان URL خلف مصادقة HTTP Basic ويولّد كلمة مرور عشوائية تُطبع على وحدة التحكم. يعتمد الإطار مبدأ الإغلاق افتراضيًا، وهو نقطة بداية أكثر أمانًا من مبدأ الفتح الافتراضي.

المفاهيم الأساسية في لمحة سريعة

يُبنى Spring Security حول ثلاث تجريدات تتكرر في كل ميزة ستتعلمها:

  1. الكيان الفاعل (Principal): الكيان المصادق عليه حاليًا (مستخدم، حساب خدمة، عميل OAuth2). يُخزَّن في SecurityContext المرتبط بالخيط الحالي.
  2. المصادقة (Authentication): كائن يمثّل بيانات الاعتماد المُقدَّمة أثناء تسجيل الدخول، وبعد التحقق منها يمثّل الهوية المؤكدة وصلاحياتها. الواجهة هي org.springframework.security.core.Authentication.
  3. الصلاحية الممنوحة (GrantedAuthority): إذن أو دور مرتبط بالكيان الفاعل المصادق عليه (مثل ROLE_ADMIN أو orders:read).

تتدفق هذه الكائنات الثلاثة عبر كل طلب وهي ما تستجوبها قواعد التفويض في نهاية المطاف.

كيف يندمج Spring Security مع تطبيق Spring Boot

يتكامل Spring Security على مستوى طبقة فلاتر Servlet. يُسجّل فلترًا وحيدًا نقطة دخول — DelegatingFilterProxy — مع حاوية Servlet، والذي يُفوّض بدوره إلى FilterChainProxy الذي يديره Spring. يحتوي هذا الوكيل على قائمة مرتبة من سلاسل فلاتر الأمان، وكل منها سلسلة من الفلاتر المتخصصة (CSRF وإدارة الجلسات والمصادقة والتفويض وغيرها).

التداعية العملية: يعمل Spring Security قبل تشغيل أي @Controller أو @RestController. الطلب الذي يفشل في المصادقة أو التفويض يُرفض على مستوى الفلتر ولا يصل أبدًا إلى كود التطبيق الخاص بك.

// Spring Boot 3 — build.gradle (Kotlin DSL) dependencies { implementation("org.springframework.boot:spring-boot-starter-security") implementation("org.springframework.boot:spring-boot-starter-web") }

هذه التبعية الواحدة هي كل ما تحتاجه. يكتشف الإعداد التلقائي لـ Spring Boot وجودها في مسار الفئات ويقوم بما يلي:

  • إنشاء UserDetailsService افتراضي بمستخدم في الذاكرة (user / كلمة مرور عشوائية).
  • تسجيل FilterChainProxy مع حاوية Servlet.
  • تفعيل HTTP Basic وتسجيل الدخول عبر النموذج على كل عنوان URL.
  • تشغيل حماية CSRF ورؤوس الأمان وحماية تثبيت الجلسة.
تحقق من تقرير الإعداد التلقائي: شغّل تطبيقك مع --debug وابحث عن SecurityAutoConfiguration في المخرجات. ستر بالضبط أي Beans تم إنشاؤها وأي شروط تحققت. هذا أمر لا يقدر بثمن عندما لا يتصرف شيء ما كما هو متوقع.

مثال عملي مبسط

لنكتب أصغر تطبيق Spring Boot 3 ممكن يُبرهن على Spring Security في العمل. لا شيء مُهيَّأ — نعتمد كليًا على الإعداد التلقائي.

package com.example.security; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SecurityDemoApplication { public static void main(String[] args) { SpringApplication.run(SecurityDemoApplication.class, args); } }
package com.example.security; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello, authenticated user!"; } }

شغّل التطبيق. في وحدة التحكم ستر سطرًا مشابهًا لما يلي:

Using generated security password: 3fa16c7d-9e02-4b8f-8e16-4c1f2d3a7b90 This generated password is for development use only. Your security configuration must be updated before running your application in production.

طلب GET /hello بدون بيانات اعتماد يُعيد استجابة 401 Unauthorized مع رأس WWW-Authenticate: Basic. أمدّ اسم المستخدم user وكلمة المرور المُولَّدة عبر HTTP Basic وسينجح الطلب. اعترض Spring Security الطلبَ وتحقق من بيانات الاعتماد وخزَّن كائن Authentication الناتج في SecurityContext ثم سمح للطلب بالمضي إلى المتحكم.

SecurityContext والمستخدم الحالي

بمجرد مصادقة طلب ما، يُعيش كائن Authentication المُتحقق منه في SecurityContextHolder المرتبط بالخيط الحالي. يمكن لأي كود يعمل على هذا الخيط — بما في ذلك متحكماتك وخدماتك — الوصول إليه:

import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MeController { @GetMapping("/me") public String currentUser() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); return "Logged in as: " + auth.getName() + " | Authorities: " + auth.getAuthorities(); } }

في التطبيقات الفعلية ستحقن كائن المصادقة كمعامل للدالة باستخدام @AuthenticationPrincipal أو عبر حقن معاملات Spring MVC، وهو أنظف وأكثر قابلية للاختبار. لكن استدعاء SecurityContextHolder المباشر يجعل النموذج الأساسي واضحًا.

لا تُشارك SecurityContext يدويًا عبر الخيوط. استراتيجية SecurityContextHolder الافتراضية (MODE_THREADLOCAL) لا تنشر السياق إلى الخيوط الفرعية أو المهام غير المتزامنة. إذا استخدمت @Async أو CompletableFuture أو الخيوط الافتراضية (virtual threads)، فانتقل إلى MODE_INHERITABLETHREADLOCAL أو استخدم غلاف DelegatingSecurityContextExecutor من Spring Security — وإلا سيكون سياق الأمان فارغًا على خيط العمل وستفشل فحوصات التفويض بصمت.

إلى أين يتجه هذا البرنامج التعليمي من هنا

مع الصورة الكاملة في مكانها، تُبنى الدروس المتبقية عليها بشكل منهجي. يُشرّح الدرس الثاني سلسلة فلاتر الأمان لتفهم ما يُنفَّذ عند كل طلب. يرسم الدرس الثالث الخط الدقيق بين المصادقة والتفويض. يُوضّح الدرس الرابع كيفية استبدال الإعداد التلقائي ببين SecurityFilterChain مخصص — الأسلوب الاصطلاحي في Spring Security 6. تتناول الدروس الخامس والسادس تحميل المستخدمين وتشفير كلمات المرور. تتناول الدروس الأخيرة قواعد التفويض وأمان مستوى الدوال وتجميع تطبيق ويب محمي بالكامل.

الخلاصة

يتكامل Spring Security على مستوى طبقة فلاتر Servlet، ويعمل قبل أي كود متحكم. إضافة تبعية spring-boot-starter-security تُفعّل وضعية أمان مغلقة افتراضيًا تحمي من CSRF وتثبيت الجلسة والضغط الخفي على النقر وهجمات استنشاق المحتوى خارج الصندوق. التجريدات الثلاث الأساسية — الكيان الفاعل والمصادقة والصلاحية الممنوحة — تتدفق عبر كل طلب وتُشكّل أساس كل ميزة ستُهيّئها في الدروس القادمة. يُخزَّن كائن Authentication المُتحقق منه في SecurityContextHolder المرتبط بالخيط ويمكن لجميع الكودات على خيط الطلب الوصول إليه.