πŸ“š[Spring] μŠ€ν”„λ§μ‹œνλ¦¬ν‹° 내뢀ꡬ쑰 -1

텁텁·2025λ…„ 6μ›” 24일

μ‹œνλ¦¬ν‹° λ‚΄λΆ€ ꡬ쑰 - 1

μ‚¬μš©μžμ˜ μš”μ²­μ„ κ°μ‹œν•˜λ €λ©΄?

  1. WAS(ν†°μΊ£)μ—μ„œ Filter Chain 을 톡해 μš”μ²­μ„ μ²˜λ¦¬ν•œλ‹€.
  2. κ·Έ 쀑 ν•˜λ‚˜κ°€ DelegatingFilterProxy (μŠ€ν”„λ§μ—μ„œ λ“±λ‘ν•œ ν•„ν„°)
  3. ν•΄λ‹Ή ν”„λ‘μ‹œ ν•„ν„°κ°€ μ‹€μ œλ‘œ μŠ€ν”„λ§ μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ˜ FilterChainProxy 둜 μš”μ²­μ„ μœ„μž„
  4. FilterChainProxy λŠ” 내뢀에 λ“±λ‘λœ SecurityFilterChain λͺ©λ‘μ„ 기반으둜 μ‹œνλ¦¬ν‹°μ˜ 인증/인가와 같은 λ‘œμ§μ„ μˆ˜ν–‰ν•œλ‹€.
  5. μ‹œνλ¦¬ν‹° ν•„ν„°λ₯Ό λ‹€ ν†΅κ³Όν•˜κ³  λ‚œ λ’€ λ‹€μ‹œ WAS 의 λ‚˜λ¨Έμ§€ ν•„ν„°λ₯Ό μˆ˜ν–‰ν•˜κ±°λ‚˜ 컨트둀러둜 μš”μ²­μ΄ μ „λ‹¬λœλ‹€.
  • DelegatingFilterProxy -> FilterChainProxy -> λ‹€μ‹œ WAS ν•„ν„° νλ¦„μœΌλ‘œ 볡귀
  • 직접 μž‘μ„±ν•˜κ±°λ‚˜ μ»€μŠ€ν…€ν•  κ²½μš°λŠ” 거의 μ—†κΈ° λ•Œλ¬Έμ— 이런ꡬ쑰둜 μ§„ν–‰λœλ‹€ μ •λ„λ§Œ μ•ŒκΈ°λ‘œ ν•˜μž.

SecurityFilterChain

μŠ€ν”„λ§ μ‹œνλ¦¬ν‹° μ˜μ‘΄μ„±μ„ μΆ”κ°€ν•˜λ©΄ μš°λ¦¬κ°€ λ”°λ‘œ μ„€μ •ν•˜μ§€ μ•Šμ„ μ‹œ κΈ°λ³Έ λ³΄μ•ˆ 섀정이 적용된 DefaultSecurityFilterChain 이 μžλ™μœΌλ‘œ λ“±λ‘λœλ‹€.
μ»€μŠ€ν„°λ§ˆμ΄μ§•μ΄ ν•„μš”ν•˜λ‹€λ©΄ SecurityFilterChain 을 λ°˜ν™˜ν•˜λŠ” @Bean λ©”μ†Œλ“œλ₯Ό μ •μ˜ν•΄ 직접 등둝할 수 μžˆλ‹€.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
	@Bean
	public SecurityFilterChain filterChain1(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity
			.authorizeHttpRequests(auth -> auth
                .requestMatchers("/admin/**").permitAll()
                .anyRequest().authenticated())
            .build();
	}
}

SecurityFilterChain 은 N개λ₯Ό 등둝할 μˆ˜λ„ 있으며 FilterChainProxy λŠ” 각 ν•„ν„°μ²΄μΈμ˜ RequestMatcher λ₯Ό μˆœμ„œλŒ€λ‘œ κ²€μ‚¬ν•˜μ—¬
κ°€μž₯ λ¨Όμ € λ§€μΉ­λ˜λŠ” ν•˜λ‚˜μ˜ 체인만 선택해 μ‹€ν–‰ν•œλ‹€.
λ”°λΌμ„œ λ“±λ‘μˆœμ„œκ°€ 맀우 μ€‘μš”ν•˜λ©° ν•„μš”μ‹œ @Order μ–΄λ…Έν…Œμ΄μ…˜μ„ 톡해 λͺ…μ‹œμ μœΌλ‘œ μˆœμ„œλ₯Ό μ œμ–΄ν•  수 μžˆλ‹€.

@Orderλ₯Ό λͺ…μ‹œν•˜μ§€ μ•ŠμœΌλ©΄ λ“±λ‘λœ μˆœμ„œλŒ€λ‘œ κ²€μ‚¬λ˜λ―€λ‘œ (/**)와 같은 λ„ˆλ¬΄ 넓은 matcher κ°€ μœ„μ— 였면 λ‹€λ₯Έ 체인이 μž‘λ™ν•˜μ§€ μ•Šμ„ 수 μžˆλ‹€.


μ„€μ • μ΄ˆλ°˜μ— .securityMatcher() λ₯Ό μ‚¬μš©ν•΄ 이 체인이 μ–΄λ–€ μš”μ²­μ— 맀칭될지 μ§€μ •ν•  수 도 μžˆλ‹€.

@Bean
public SecurityFilterChain adminFilterChain(HttpSecurity http) throws Exception {
    return http
        .securityMatcher("/admin/**")  // RequestMatcher μ—­ν• : 이 체인은 /admin/** μš”μ²­μ—λ§Œ μ μš©ν•œλ‹€λŠ” 뜻
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        .build();
}

μ‹œνλ¦¬ν‹° 제곡 ν•„ν„° 간단 μ„€λͺ…

  • DisableEncodeUrlFilter
    • URL둜 κ°„μ£Όλ˜μ§€ μ•ŠλŠ” 뢀뢄을 ν¬ν•¨ν•˜μ§€ μ•Šλ„λ‘ μ„€μ •
  • WebAsyncManagerIntegrationFilter
    • Spring MVC 의 @Async, Callable, DeferredResult 같은 비동기 μ²˜λ¦¬μ™€ μ‹œνλ¦¬ν‹° μ»¨ν…μŠ€νŠΈλ₯Ό μ—°κ²°
  • SecurityContextHolderFilter
    • 인증된 μ‚¬μš©μž 정보λ₯Ό SecurityContextHolder에 μ €μž₯ν•˜κ³  μ“°λ ˆλ“œ κ°„ λ³΄μ•ˆ μ»¨ν…μŠ€νŠΈ μœ μ§€
  • HeaderWriterFilter
    • λ³΄μ•ˆ κ΄€λ ¨ HTTP 헀더λ₯Ό 응닡에 μΆ”κ°€(예: X-Content-Type-Options, X-Frame-Options, Cache-Control)
    • μŠ€ν”„λ§ μ‹œνλ¦¬ν‹°μ˜ 헀더 κ΄€λ ¨ 섀정은 λŒ€λΆ€λΆ„ μ—¬κΈ°μ„œ 처리
  • CorsFilter
    • ꡐ차 좜처 μš”μ²­(CORS)에 λŒ€ν•œ 처리
  • CsrfFilter
    • CSRF(Cross Site Request Forgery) 곡격을 λ°©μ§€ν•˜κΈ° μœ„ν•œ ν•„ν„°
    • μ„œλ²„κ°€ μ œκ³΅ν•œ 토큰과 ν΄λΌμ΄μ–ΈνŠΈ μš”μ²­μ˜ 토큰을 비ꡐ
  • LogoutFilter
    • /logout μš”μ²­μ„ μ²˜λ¦¬ν•˜λŠ” μ‹œμž‘μ 
    • λ‘œκ·Έμ•„μ›ƒ μ‹œ μ„Έμ…˜ λ¬΄νš¨ν™”, SecurityContext μ΄ˆκΈ°ν™”, 인증 μΏ ν‚€ μ‚­μ œ λ“±
    • 기본은 GET: /logout 이며 μ»€μŠ€ν„°λ§ˆμ΄μ§• κ°€λŠ₯ν•˜λ‹€.
  • UsernamePasswordAuthenticationFilter
    • /login μš”μ²­μ„ μ²˜λ¦¬ν•˜λŠ” ν•„ν„°
    • μ‚¬μš©μžκ°€ μ œμΆœν•œ usernameκ³Ό passwordλ₯Ό μΆ”μΆœν•΄ AuthenticationManager에 인증을 μš”μ²­ν•˜μ—¬ μ„±κ³΅μ‹œ SecurityContext에 인증정보 μ €μž₯
  • DefaultLoginPageGeneratingFilter
    • λ³„λ„μ˜ 둜그인 νŽ˜μ΄μ§€λ₯Ό λ§Œλ“€μ§€ μ•Šμ•˜μ„ λ•Œ κΈ°λ³Έ 둜그인 νŽ˜μ΄μ§€ GET: /login을 μƒμ„±ν•˜μ—¬ 응닡
    • λΉ„ν™œμ„±ν™”ν•˜κ±°λ‚˜ μ»€μŠ€ν„°λ§ˆμ΄μ§• κ°€λŠ₯
  • DefaultLogoutPageGeneratingFilter
    • κΈ°λ³Έ λ‘œκ·Έμ•„μ›ƒ νŽ˜μ΄μ§€ 생성 GET: /logout
    • μ»€μŠ€ν„°λ§ˆμ΄μ§• κ°€λŠ₯
  • BasicAuthenticationFilter
    • Authorization: Basic μ΄ν•˜ν† ν° 헀더가 ν¬ν•¨λœ μš”μ²­μ„ 처리
    • HTTP Basic 인증 기반으둜 ν—€λ”μ—μ„œ username/password μΆ”μΆœ ν›„ 인증 처리
  • RequestCacheAwareFilter
    • 인증 ν›„ μ›λž˜ μ ‘κ·Όν•˜λ €λ˜ URL이 μžˆμ„ 경우 ν•΄λ‹Ή URL둜 redirect
    • 인증 도쀑 μš”μ²­μ΄ μΈν„°μ…‰νŠΈλ˜λ©΄ 성곡 ν›„ μš”μ²­μœΌλ‘œ λ¦¬λ‹€μ΄λ ‰νŠΈν•˜λ„λ‘ λ„μ™€μ€Œ
  • SecurityContextHolderAwareRequestFilter
    • μ„œλΈ”λ¦Ώ APIμ—μ„œ μ œκ³΅ν•˜λŠ” λ³΄μ•ˆ κ΄€λ ¨ λ©”μ„œλ“œλ“€μ„ μ‹œνλ¦¬ν‹° μ»¨ν…μŠ€νŠΈμ™€ 연동
  • AnonymousAuthenticationFilter
    • μΈμ¦λ˜μ§€ μ•Šμ€ μš”μ²­μ— λŒ€ν•΄ μžλ™μœΌλ‘œ 읡λͺ… μ‚¬μš©μžλ‘œ μ„€μ •
    • SecurityContext κ°€ λΉ„μ–΄μžˆμ„ 경우 읡λͺ… 인증 객체(AnonymousAuthenticationToken)λ₯Ό μ‚½μž…
    • λ”°λΌμ„œ μ‹œνλ¦¬ν‹°λŠ” 항상 인증 객체λ₯Ό κ°€μ§€κ³  λ™μž‘
  • ExceptionTranslationFilter
    • 인증 μ‹€νŒ¨λ‚˜ 인가 μ‹€νŒ¨ μ˜ˆμ™Έμ— λŒ€ν•œ μ μ ˆν•œ 처리 μˆ˜ν–‰
      • 인증 μ‹€νŒ¨ -> 둜그인 νŽ˜μ΄μ§€λ‘œ redirect
      • 인가 μ‹€νŒ¨ -> 403 μ—λŸ¬ λ°˜ν™˜
    • μ‹œνλ¦¬ν‹° ν•„ν„° 쀑 μ˜ˆμ™Έ ν•Έλ“€λ§μ˜ 핡심 ν•„ν„°
  • AuthorizationFilter
    • μ΅œμ’… 인가 둜직 μˆ˜ν–‰ : 경둜, κΆŒν•œ, μ—­ν•  λ“± 정책에 따라 μ ‘κ·Ό κ°€λŠ₯ μ—¬λΆ€ νŒλ‹¨
    • 컨트둀러 μ‹€ν–‰ μ „ 인가 λ‘œμ§μ— 따라 접근을 ν—ˆμš©ν•˜κ±°λ‚˜ 차단

SecurityFilterChain ν•„ν„° ν™œμ„±ν™”/λΉ„ν™œμ„±ν™”

@Bean
public SecurityFilterChain customFilterChain(HttpSecurity http) throws Exception {
    http
		// λΉ„ν™œμ„±ν™” μ˜ˆμ‹œ
		.cors(cors -> cors.disable()) // CorsFilter λΉ„ν™œμ„±ν™”
		.csrf(csrf -> csrf.disable()) // CsrfFilter λΉ„ν™œμ„±ν™”
		.httpBasic(httpBasic -> httpBasic.disable()) // BasicAuthenticationFilter λΉ„ν™œμ„±ν™”

		// ν™œμ„±ν™” μ˜ˆμ‹œ
		.formLogin(withDefaults()) // UsernamePasswordAuthenticationFilter ν™œμ„±ν™” (κΈ°λ³Έ 둜그인 폼)
		.logout(withDefaults()); // LogoutFilter ν™œμ„±ν™” (κΈ°λ³Έ λ‘œκ·Έμ•„μ›ƒ 처리)
    
	return http.build();
}

csrf, formLogin, logout λ“± 일뢀 ν•„ν„°λŠ” μƒλž΅ν•˜λ©΄ μžλ™μœΌλ‘œ ν™œμ„±ν™”λ˜λŠ” κ²½μš°λ„ 있으며
λͺ…μ‹œμ μœΌλ‘œ ν™œμ„±ν™” ν•˜κΈ° μœ„ν•΄μ„  withDefaults() λ˜λŠ” μ»€μŠ€ν…€ 섀정을 λ„£μ–΄μ£Όλ©΄ λœλ‹€.
이처럼 기본적으둜 λ©”μ„œλ“œλ₯Ό 톡해 ν™œμ„±/λΉ„ν™œμ„±ν™”λ₯Ό μ‘°μ • ν•  수 μžˆλ‹€.

withDefaults()λž€?

  • formLogin(withDefaults()) == formLogin(form -> {})
  • λ‚΄λΆ€μ μœΌλ‘œλŠ” 빈 μ„€μ • 블둝을 λ„£λŠ” 것과 κ°™μ•„μ„œ κΈ°λ³Έ λ™μž‘μ„ ν™œμ„±ν™” ν•œλ‹€λŠ” 것을 λœ»ν•œλ‹€.

SecurityFilterChain 에 μ»€μŠ€ν…€ ν•„ν„° 등둝

  1. νŠΉμ •ν•„ν„° 이전
    • .addFilterBefore(μΆ”κ°€ν• ν•„ν„°, κΈ°μ‘΄ν•„ν„°.class)
  2. νŠΉμ •ν•„ν„° μœ„μΉ˜
    • .addFilterAt(μΆ”κ°€ν• ν•„ν„°, κΈ°μ‘΄ν•„ν„°.class)
  3. νŠΉμ •ν•„ν„° 이후
    • .addFilterAfter(μΆ”κ°€ν• ν•„ν„°, κΈ°μ‘΄ν•„ν„°.class)

2νŽΈμ€ SecurityContextHolder 둜 μ΄μ–΄μ§‘λ‹ˆλ‹€.

References

  • https://www.youtube.com/@xxxjjhhh
    개발자 μœ λ―Έλ‹˜ 유튜브 - μŠ€ν”„λ§ μ‹œνλ¦¬ν‹° 내뢀ꡬ쑰 1 ~ 5
profile
μ°¨κ·Όμ°¨κ·Ό

0개의 λŒ“κΈ€