Spring Boot Security 설정 패턴
백엔드Spring Boot Security 설정 패턴 — 실전 적용 구조와 코드 예시
언제 쓰나 · SPA + JWT 구조의 Spring Boot API 서비스 · JWT / API Key 이중 인증이 필요할 때
#backend#Spring#Boot#Security
# Spring Boot Security 설정 패턴
## 언제 쓰는가
- SPA + JWT 구조의 Spring Boot API 서비스
- JWT / API Key 이중 인증이 필요할 때
## 핵심 구조
- SecurityFilterChain (Stateless + CSRF 비활성화)
- 화이트리스트 방식 엔드포인트 접근 제어
- 커스텀 필터 체인 (API Key → JWT 순서)
- CORS 환경변수 주입
## 코드 예시
### SecurityConfig
```java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/**").authenticated()
.anyRequest().denyAll()
)
.exceptionHandling(ex ->
ex.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)))
.addFilterBefore(apiKeyFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
```
### API Key 필터 (접두사 + BCrypt)
```java
String key = extractApiKey(req); // Bearer xxx 또는 X-API-Key: xxx
String prefix = key.substring(0, 8);
List<ApiKey> candidates = repo.findByKeyPrefixAndIsActiveTrue(prefix);
for (ApiKey c : candidates) {
if (passwordEncoder.matches(key, c.getKeyHash())) {
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(userId, null, authorities));
break;
}
}
```
### CORS (환경변수 기반)
```java
@Value("${app.cors.allowed-origins}")
private String allowedOrigins; // 쉼표 구분
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(Arrays.asList(allowedOrigins.split(",")));
config.setAllowedHeaders(List.of("Authorization", "Content-Type", "X-API-Key"));
config.setAllowCredentials(true);
config.setMaxAge(3600L);
```
## 주의사항
- `anyRequest().denyAll()` 필수 — 명시하지 않은 경로 전부 차단
- `PasswordEncoder`는 별도 `@Configuration`으로 분리 (순환의존성 방지)
- CORS에 와일드카드(`*`) 대신 명시적 헤더 나열
- API Key는 DB에 BCrypt 해시만 저장, 평문 미저장
- Actuator는 `health`, `info`만 노출
- 이중 인증 불필요 시 API Key 필터 제거하고 JWT만 사용
## 재사용 방법
- SecurityConfig + JwtFilter 복사 → 경로 매칭만 수정
- 이중 인증 필요 시 ApiKeyFilter 추가
- CORS 허용 오리진은 환경변수로 주입jt · v1 · CC0-1.0 · 복사 0