انتقل إلى المحتوى الرئيسي

الفصل 1: مقدمة إلى Spring Security

مقدمة​

قبل إعداد أي شيء، من الضروري فهم كيفية عمل Spring Security. هذا الفصل يضع الأسس التي ستسمح لك بإعداد الأمان بشكل صحيح.


🎯 أهداف الفصل​

في نهاية هذا الفصل، ستعرف:

  1. ✅ لماذا الأمان ضروري
  2. ✅ الفرق بين Authentication و Authorization
  3. ✅ بنية Spring Security
  4. ✅ كيف تعمل سلسلة الفلاتر
  5. ✅ المفاهيم الأساسية: Principal، Credentials، Authorities

1. لماذا تأمين تطبيق؟​

التهديدات​

أمثلة حقيقية​

السنةالشركةالتأثير
2023MOVEitأكثر من 60 مليون مستخدم متأثر
2021Facebook533 مليون حساب مكشوف
2019Capital Oneأكثر من 100 مليون بيانات مسروقة
2017Equifax147 مليون بيانات شخصية

الركائز الثلاثة للأمان (CIA)​

┌─────────────────────────────────────────────────┐
│ │
│ C - السرية │
│ فقط الأشخاص المصرح لهم يمكنهم رؤية البيانات │
│ │
│ I - النزاهة │
│ لا يمكن تعديل البيانات بدون إذن │
│ │
│ A - التوفر │
│ يبقى النظام متاحًا للمستخدمين الشرعيين │
│ │
└─────────────────────────────────────────────────┘

2. Authentication vs Authorization​

الفرق الأساسي​

المفهومالسؤالالمثال
Authentication"من أنت؟"تسجيل الدخول بالبريد الإلكتروني/كلمة المرور
Authorization"هل لديك الحق؟"الوصول إلى /admin (محجوز للمسؤولين)

تشبيه: نادي VIP​

في Spring Security​

// Authentication: من هو المتصل؟
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName(); // "jean@example.com"

// Authorization: ما هي حقوقه؟
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
// [ROLE_USER] - ليس ROLE_ADMIN!

3. بنية Spring Security​

نظرة عامة​

المكونات الرئيسية​

1. SecurityFilterChain​

سلسلة الفلاتر التي تعترض جميع الطلبات:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.build();
}
}

2. AuthenticationManager​

ينسق عملية التحقق:

3. UserDetailsService​

واجهة لتحميل المستخدمين:

public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

4. PasswordEncoder​

يشفر كلمات المرور (أبدًا بشكل واضح!):

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

// الاستخدام
String encoded = passwordEncoder.encode("monMotDePasse");
// $2a$10$N9qo8uLOickgx2ZMRZoMye...

boolean matches = passwordEncoder.matches("monMotDePasse", encoded);
// true

4. سلسلة الفلاتر بالتفصيل​

ترتيب الفلاتر (مبسط)​

1. CorsFilter                    → إدارة CORS
2. CsrfFilter → حماية CSRF
3. LogoutFilter → إدارة تسجيل الخروج
4. UsernamePasswordAuthFilter → تسجيل الدخول عبر النموذج
5. BasicAuthenticationFilter → HTTP Basic Auth
6. BearerTokenAuthFilter → JWT/OAuth2
7. AnonymousAuthenticationFilter → مستخدم مجهول
8. ExceptionTranslationFilter → إدارة الأخطاء
9. FilterSecurityInterceptor → قرار الوصول النهائي

تصور التدفق​


5. المفاهيم الأساسية​

Principal​

هوية المستخدم المتصل:

@GetMapping("/profile")
public String profile(Principal principal) {
String username = principal.getName();
return "مرحبًا بك " + username;
}

كائن Authentication​

يحتوي على جميع معلومات التحقق:

public interface Authentication {
Collection<? extends GrantedAuthority> getAuthorities(); // الأدوار/الأذونات
Object getCredentials(); // كلمة المرور (غالبًا ما يتم مسحها بعد التحقق)
Object getDetails(); // تفاصيل إضافية (IP، إلخ)
Object getPrincipal(); // المستخدم (UserDetails)
boolean isAuthenticated();
}

GrantedAuthority​

يمثل دورًا أو إذنًا:

// الأدوار (بادئة ROLE_)
new SimpleGrantedAuthority("ROLE_ADMIN");
new SimpleGrantedAuthority("ROLE_USER");

// الأذونات
new SimpleGrantedAuthority("READ_USERS");
new SimpleGrantedAuthority("WRITE_USERS");
new SimpleGrantedAuthority("DELETE_USERS");

SecurityContext​

يخزن Authentication للمستخدم الحالي:

// استرجاع المستخدم المتصل
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();

if (auth != null && auth.isAuthenticated()) {
String username = auth.getName();
// ...
}

UserDetails​

واجهة تمثل مستخدمًا:

public interface UserDetails {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}

6. أنواع التحقق​

1. تسجيل الدخول بالنموذج (الجلسة)​

العميل → POST /login (user, pass)
الخادم → Set-Cookie: JSESSIONID=xyz
العميل → GET /api/data (Cookie: JSESSIONID=xyz)
الخادم → تحقق من الجلسة → 200 OK

المزايا: بسيط، يُدار بواسطة المتصفح العيوب: غير مناسب لـ API REST عديم الحالة

2. HTTP Basic​

العميل → GET /api/data
الخادم → 401 + WWW-Authenticate: Basic
العميل → GET /api/data (Authorization: Basic base64(user:pass))
الخادم → 200 OK

المزايا: بسيط العيوب: يتم إرسال credentials مع كل طلب

3. JWT (Token)​

العميل → POST /login (user, pass)
الخادم → {"token": "eyJhbGciOiJIUzI1..."}
العميل → GET /api/data (Authorization: Bearer eyJhbGciOiJIUzI1...)
الخادم → تحقق من token → 200 OK

المزايا: عديم الحالة، مثالي لـ API REST العيوب: إدارة انتهاء الصلاحية

4. OAuth2​

العميل → إعادة توجيه إلى Google
Google → تسجيل دخول المستخدم
Google → إعادة توجيه مع رمز
الخادم → تبادل الرمز مقابل token
الخادم → استرجاع ملف تعريف المستخدم

المزايا: لا حاجة لإدارة كلمة المرور، تجربة مستخدم حديثة العيوب: اعتماد خارجي


7. التكوين الحديث (Spring Security 6+)​

الطريقة القديمة (مهملة)​

// ❌ لا تستخدم
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated();
}
}

الطريقة الجديدة (Spring Security 6+)​

// ✅ استخدم هذا النهج
@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults())
.build();
}

@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}

8. التكوين الأولي​

تبعية Maven​

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

السلوك الافتراضي​

بدون أي تكوين، يقوم Spring Security بـ:

  • حماية جميع النقاط النهائية
  • توليد كلمة مرور عشوائية (تظهر في السجلات)
  • تفعيل تسجيل الدخول بالنموذج على /login
  • تفعيل تسجيل الخروج على /logout
Using generated security password: 8e3d7a2c-1234-5678-abcd-ef0123456789

التكوين الأدنى​

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/home", "/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout
.permitAll()
)
.build();
}
}

9. الملخص​

المفاهيم الأساسية​

المفهومالوصف
Authenticationالتحقق من الهوية (من؟)
Authorizationالتحقق من الحقوق (ماذا؟)
SecurityFilterChainسلسلة فلاتر الأمان
AuthenticationManagerينظم عملية التحقق
UserDetailsServiceتحميل المستخدمين
PasswordEncoderتشفير كلمات المرور
SecurityContextتخزين المستخدم الحالي

بنية مبسطة​

طلب HTTP
↓
SecurityFilterChain (فلاتر)
↓
AuthenticationManager
↓
UserDetailsService + PasswordEncoder
↓
SecurityContext (تخزين)
↓
التطبيق (Controller)

نقاط مهمة​

✅ Spring Security 6+ يستخدم @Bean، وليس extends WebSecurityConfigurerAdapter
✅ دائمًا تشفير كلمات المرور (BCrypt)
✅ فهم Authentication vs Authorization
✅ يتم تكوين الأمان عبر SecurityFilterChain

🎯 الخطوة التالية​

الآن بعد أن فهمت البنية، دعنا ننتقل إلى الممارسة مع التحقق الأساسي.

➡️ الفصل 2: التحقق الأساسي


⬅️ العودة إلى الوحدة 8