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

اختبار: Spring Security

اختبر معرفتك بأمان Spring، JWT وOAuth2.

30 سؤالاً - انقر على كل سؤال لكشف الإجابة.


الأسئلة 1-10: أساسيات الأمان​

السؤال 1: ما هي التبعية التي يجب إضافتها لـ Spring Security؟

الإجابة:

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

بشكل افتراضي، جميع المسارات محمية.


السؤال 2: ماذا يحدث عند إضافة Spring Security بدون تكوين؟

الإجابة:

  • جميع المسارات تتطلب مصادقة
  • يتم عرض نموذج تسجيل دخول
  • يتم إنشاء مستخدم user بكلمة مرور مولدة (معروضة في السجلات)
  • يتم تفعيل CSRF

السؤال 3: كيف يتم تكوين الأمان في Spring Boot 3؟

الإجابة:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults());

return http.build();
}
}

السؤال 4: ما الفرق بين authenticated() و permitAll()؟

الإجابة:

  • permitAll(): يمكن الوصول إليه بدون مصادقة
  • authenticated(): يتطلب مستخدمًا مصادقًا
  • hasRole("X"): يتطلب الدور X
  • hasAuthority("X"): يتطلب السلطة X

السؤال 5: كيف يتم تعريف المستخدمين في الذاكرة؟

الإجابة:

@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();

UserDetails admin = User.builder()
.username("admin")
.password(passwordEncoder().encode("admin"))
.roles("ADMIN")
.build();

return new InMemoryUserDetailsManager(user, admin);
}

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

السؤال 6: كيف يتم تنفيذ UserDetailsService مع قاعدة بيانات؟

الإجابة:

@Service
public class CustomUserDetailsService implements UserDetailsService {

private final UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));

return org.springframework.security.core.userdetails.User.builder()
.username(user.getEmail())
.password(user.getPassword())
.roles(user.getRole().name())
.build();
}
}

السؤال 7: لماذا يتم تشفير كلمات المرور؟

الإجابة:

  • الأمان: كلمات المرور غير قابلة للقراءة إذا تم اختراق قاعدة البيانات
  • BCrypt: يولد تجزئة مختلفة في كل مرة (ملح مدمج)
  • إلزام: Spring Security يرفض كلمات المرور النصية
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

السؤال 8: ما الفرق بين Role و Authority؟

الإجابة:

  • Role: يتم إضافة البادئة ROLE_ تلقائيًا
  • Authority: يتم استخدامها كما هي
// هذان متكافئان:
.hasRole("ADMIN") // يتحقق من ROLE_ADMIN
.hasAuthority("ROLE_ADMIN")

// سلطات مخصصة
.hasAuthority("READ_USERS")
.hasAuthority("WRITE_USERS")

السؤال 9: ما هو CSRF ومتى يتم تعطيله؟

الإجابة: CSRF (Cross-Site Request Forgery) يحمي من الطلبات الضارة.

تعطيله لـ API REST بدون حالة (JWT):

http.csrf(csrf -> csrf.disable())

ابقه مفعلاً للتطبيقات ذات الجلسات.


السؤال 10: كيف يتم استرداد المستخدم المتصل؟

الإجابة:

// في وحدة تحكم
@GetMapping("/me")
public User getCurrentUser(@AuthenticationPrincipal UserDetails userDetails) {
return userService.findByEmail(userDetails.getUsername());
}

// أو عبر SecurityContextHolder
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = auth.getName();

الأسئلة 11-20: JWT​

السؤال 11: ما هو JWT؟

الإجابة: JSON Web Token - رمز مكون من 3 أجزاء:

  1. Header: الخوارزمية والنوع
  2. Payload: المطالبات (البيانات)
  3. Signature: التحقق من السلامة

التنسيق: header.payload.signature (مشفرة Base64)


السؤال 12: ما هي المطالبات القياسية لـ JWT؟

الإجابة:

المطالبةالوصف
subالموضوع (المستخدم)
issالمصدر (المرسل)
expانتهاء الصلاحية
iatتم الإصدار في
nbfليس قبل
jtiمعرف JWT (فريد)

السؤال 13: كيف يتم توليد JWT؟

الإجابة:

@Service
public class JwtService {

@Value("${jwt.secret}")
private String secret;

public String generateToken(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}

private Key getSigningKey() {
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
}
}

السؤال 14: كيف يتم التحقق من صحة JWT؟

الإجابة:

public boolean isTokenValid(String token, UserDetails userDetails) {
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();

String username = claims.getSubject();
Date expiration = claims.getExpiration();

return username.equals(userDetails.getUsername())
&& !expiration.before(new Date());
} catch (JwtException e) {
return false;
}
}

السؤال 15: كيف يتم تنفيذ فلتر JWT؟

الإجابة:

@Component
public class JwtAuthFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain) {

String header = request.getHeader("Authorization");

if (header != null && header.startsWith("Bearer ")) {
String token = header.substring(7);
String username = jwtService.extractUsername(token);

if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails user = userDetailsService.loadUserByUsername(username);

if (jwtService.isTokenValid(token, user)) {
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
}

chain.doFilter(request, response);
}
}

السؤال 16: كيف يتم تكوين فلتر JWT؟

الإجابة:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
}

السؤال 17: ما هو Refresh Token؟

الإجابة: رمز طويل الأمد للحصول على رموز وصول جديدة بدون إعادة تسجيل الدخول:

  • Access Token: مدة قصيرة (15 دقيقة - 1 ساعة)
  • Refresh Token: مدة طويلة (7-30 يومًا)
// نقطة نهاية للتحديث
@PostMapping("/refresh")
public TokenResponse refresh(@RequestBody RefreshRequest request) {
String refreshToken = request.getRefreshToken();
// تحقق وتوليد رمز وصول جديد
}

السؤال 18: كيف يتم تخزين سر JWT بشكل آمن؟

الإجابة:

  1. متغيرات البيئة
  2. Vault (HashiCorp Vault)
  3. AWS Secrets Manager
  4. Spring Cloud Config (مشفرة)
# لا تقم أبدًا بارتكاب السر!
jwt:
secret: ${JWT_SECRET}

السؤال 19: كيف يتم إبطال JWT؟

الإجابة: JWT غير مرتبطة بالحالة، لذا لا يمكن إبطالها مباشرة. الحلول:

  1. القائمة السوداء: تخزين الرموز الملغاة (Redis)
  2. انتهاء صلاحية قصير: الرموز صالحة لفترة قصيرة
  3. إصدار المستخدم: إبطال في كل تغيير لكلمة المرور

السؤال 20: كيف يتم تأمين نقاط النهاية باستخدام التعليقات التوضيحية؟

الإجابة:

@EnableMethodSecurity
@Configuration
public class SecurityConfig { }

@RestController
public class UserController {

@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/admin/users")
public List<User> getAllUsers() { }

@PreAuthorize("#id == authentication.principal.id or hasRole('ADMIN')")
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) { }
}

الأسئلة 21-30: OAuth2 والأمان المتقدم​

السؤال 21: ما هو OAuth2؟

الإجابة: بروتوكول تفويض يسمح لتطبيق بالوصول إلى موارد مستخدم عبر مزود طرف ثالث (Google، GitHub، إلخ) بدون مشاركة كلمة المرور.


السؤال 22: ما هي الأدوار في OAuth2؟

الإجابة:

  • Resource Owner: المستخدم
  • Client: التطبيق
  • Authorization Server: Google، GitHub...
  • Resource Server: API المحمية

السؤال 23: كيف يتم تكوين OAuth2 مع Google؟

الإجابة:

spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
scope: openid, profile, email
http.oauth2Login(Customizer.withDefaults());

السؤال 24: كيف يتم تخصيص نجاح تسجيل الدخول OAuth2؟

الإجابة:

http.oauth2Login(oauth -> oauth
.successHandler((request, response, authentication) -> {
OAuth2User oauthUser = (OAuth2User) authentication.getPrincipal();
userService.processOAuthUser(oauthUser);
response.sendRedirect("/dashboard");
})
);

السؤال 25: ما هو CORS؟

الإجابة: Cross-Origin Resource Sharing - يسمح بالطلبات من نطاقات أخرى:

@Configuration
public class CorsConfig {

@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}

السؤال 26: كيف يتم إضافة رؤوس الأمان؟

الإجابة:

http.headers(headers -> headers
.contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'"))
.frameOptions(frame -> frame.deny())
.xssProtection(xss -> xss.enable())
);

السؤال 27: كيف يتم تحديد محاولات تسجيل الدخول؟

الإجابة: تنفيذ محدد معدل:

@Component
public class LoginAttemptService {

private final LoadingCache<String, Integer> attemptsCache;

public void loginFailed(String ip) {
int attempts = attemptsCache.get(ip);
attemptsCache.put(ip, attempts + 1);
}

public boolean isBlocked(String ip) {
return attemptsCache.get(ip) >= MAX_ATTEMPTS;
}
}

السؤال 28: كيف يتم تدقيق الوصول الآمن؟

الإجابة:

@Component
public class SecurityAuditListener {

@EventListener
public void onAuthSuccess(AuthenticationSuccessEvent event) {
log.info("Login successful: {}", event.getAuthentication().getName());
}

@EventListener
public void onAuthFailure(AuthenticationFailureBadCredentialsEvent event) {
log.warn("Login failed: {}", event.getAuthentication().getName());
}
}

السؤال 29: كيف يتم اختبار الأمان؟

الإجابة:

@WebMvcTest(UserController.class)
@Import(SecurityConfig.class)
class UserControllerSecurityTest {

@Test
@WithMockUser(roles = "ADMIN")
void adminCanAccessAdminEndpoint() {
mockMvc.perform(get("/admin/users"))
.andExpect(status().isOk());
}

@Test
@WithMockUser(roles = "USER")
void userCannotAccessAdminEndpoint() {
mockMvc.perform(get("/admin/users"))
.andExpect(status().isForbidden());
}
}

السؤال 30: ما هي أفضل ممارسات الأمان في Spring؟

الإجابة:

  1. تشفير كلمات المرور دائمًا (BCrypt)
  2. استخدام HTTPS في الإنتاج
  3. تكوين CORS بشكل صارم
  4. تنفيذ تحديد المعدل
  5. التحقق من جميع المدخلات
  6. تسجيل أحداث الأمان
  7. استخدام أسرار قوية وتخزينها خارجيًا
  8. تحديث التبعيات بانتظام

النتيجة​

النتيجةالمستوى
27-30ممتاز - أمان متقن
21-26جيد - بعض المراجعات
15-20متوسط - أعد قراءة الفصول
أقل من 15بحاجة إلى مراجعة

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