Spring BootSeniorTechnical
Как защитить REST API на Spring Boot с помощью Spring Security?
Настраивается через SecurityFilterChain: CSRF отключается, SessionCreationPolicy.STATELESS, правила authorizeHttpRequests по ролям, @EnableMethodSecurity для @PreAuthorize, кастомный AuthenticationEntryPoint возвращает 401 вместо редиректа.
Минимальная конфигурация SecurityFilterChain
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // включает @PreAuthorize, @PostAuthorize
public class ApiSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
// REST API не использует сессии и CSRF-куки
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(s ->
s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// правила авторизации
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
// возврат 401/403 вместо редиректа на /login
.exceptionHandling(ex -> ex
.authenticationEntryPoint((req, res, e) ->
res.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.accessDeniedHandler((req, res, e) ->
res.sendError(HttpServletResponse.SC_FORBIDDEN))
)
.build();
}
}
RBAC через @PreAuthorize
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@GetMapping
@PreAuthorize("hasAnyRole('USER','ADMIN')")
public List<Order> list() { ... }
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN') or @orderService.isOwner(#id, authentication.name)")
public void delete(@PathVariable UUID id) { ... }
}
Защита от распространённых атак
Rate limiting — через Bucket4j + Redis:
@Component
public class RateLimitFilter extends OncePerRequestFilter {
private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();
@Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain)
throws ServletException, IOException {
String ip = req.getRemoteAddr();
Bucket bucket = buckets.computeIfAbsent(ip, k ->
Bucket.builder()
.addLimit(Bandwidth.classic(100,
Refill.greedy(100, Duration.ofMinutes(1))))
.build());
if (bucket.tryConsume(1)) {
chain.doFilter(req, res);
} else {
res.setStatus(429);
}
}
}
Security headers через application.yml или конфигурацию:
.headers(h -> h
.contentSecurityPolicy(csp ->
csp.policyDirectives("default-src 'self'"))
.frameOptions(HeadersConfigurer.FrameOptionsConfig::deny)
.httpStrictTransportSecurity(hsts ->
hsts.includeSubDomains(true).maxAgeInSeconds(31536000))
)
Настройка CORS для фронтенда
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://app.example.com"));
config.setAllowedMethods(List.of("GET","POST","PUT","DELETE","OPTIONS"));
config.setAllowedHeaders(List.of("Authorization","Content-Type"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return source;
}
Подводные камни
- Отключать CSRF для REST правильно только при STATELESS-сессиях; если вы используете cookie-сессии — CSRF нужен.
- Порядок правил в
authorizeHttpRequestsважен: более специфичные паттерны должны идти раньшеanyRequest(). @EnableMethodSecurityне работает без проксирования — вызов защищённого метода из того же бина обходит проверку.- Без кастомного
AuthenticationEntryPointSpring Security редиректит на/login— для REST API это 302, а не 401. - CORS-конфигурация в Spring Security и в
@CrossOriginмогут конфликтовать — используйте один источник истины. - Логировать тело запроса в фильтре безопасности рискованно — можно случайно залогировать пароль или токен.
- Actuator-эндпоинты (
/actuator/**) нужно явно закрыть или ограничить рольюACTUATOR.
Common mistakes
- Путать термин «spring security rest api» с соседним механизмом Spring Boot.
- Не называть границу lifecycle, transaction, thread или request для «spring security rest api».
- Игнорировать production-эффекты «spring security rest api»: latency, SQL shape, memory, security или observability.
What the interviewer is testing
- Попросить объяснить механизм «spring security rest api» на минимальном примере.
- Проверить, видит ли кандидат failure mode и диагностику для «spring security rest api».
- Уточнить, какие настройки или API меняют «spring security rest api» в реальном сервисе.