μ΄λ²ˆμ—λŠ” SecurityFilterChain 의 등둝 과정에 λŒ€ν•˜μ—¬ λΆ„μ„ν•΄λ³΄μž.

Default SecurityFilterChain

Spring Security 에 λŒ€ν•œ Dependency λ₯Ό μ„€μ •ν•˜λ©΄ 기본적인 SecurityFilterChain 이 ν•˜λ‚˜ μƒμ„±λœλ‹€.

이전 κΈ€μ—μ„œ FilterChainProxy λŠ” SecurityFilterChain 듀을 List ν˜•νƒœλ‘œ κ΄€λ¦¬ν•œλ‹€κ³  ν–ˆλ‹€. μ‹€μ œλ‘œ κ΅¬ν˜„μ„ μ‚΄νŽ΄λ³΄λ©΄ μœ„μ™€ 같이 List 둜 λ˜μ–΄μžˆλŠ” 것을 λ³Ό 수 μžˆλ‹€.

FilterChainProxy.class μ—μ„œ getFitlers() method 에 breakpoint λ₯Ό κ±Έκ³  Debug λͺ¨λ“œλ‘œ μ‹€ν–‰ν•˜κ³ , κ°„λ‹¨ν•œ request λ₯Ό 보내보면 μ•„λž˜μ™€ 같이 filterChains 의 size κ°€ 1, 즉 기본적으둜 ν•˜λ‚˜μ˜ SecurityFilterChain 이 μ‘΄μž¬ν•˜λŠ” 것을 λ³Ό 수 μžˆλ‹€.

Custom SecurityFilterChain

μ΄λ²ˆμ—λŠ” Custom SecurityFilterChain 에 λŒ€ν•˜μ—¬ μ•Œμ•„λ³΄μž.

Custom SecurityFilterChain 을 λ“±λ‘ν•˜λŠ” 방법은 SecurityConfig class λ₯Ό 직접 κ΅¬μ„±ν•˜λŠ” 것이닀.

@Bean  
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
	// Filter Chains ...
	return http.build();
}

μœ„μ™€ 같이 SecurityFilterChain type 의 Bean 을 define ν•˜μ—¬ Custom SecurityFilterChain 을 등둝할 수 있으며, μ—¬λŸ¬ 개λ₯Ό define ν•˜μ—¬ μ—¬λŸ¬ 개의 SecurityFilterChain 을 등둝할 수 μžˆλ‹€. μ—¬λŸ¬ 개λ₯Ό 등둝할 경우, μ•„λž˜μ™€ 같이 size κ°€ λŠ˜μ–΄λ‚œ 것을 λ³Ό 수 μžˆλ‹€.

private List<SecurityFilterChain> filterChains;

λ˜ν•œ μœ„μ—μ„œ μ–ΈκΈ‰ν•œ κ²ƒμ²˜λŸΌ FilterChainProxy λŠ” μ—¬λŸ¬ 개의 SecurityFilterChain 듀을 List ν˜•νƒœλ‘œ κ΄€λ¦¬ν•˜λŠ”λ°, index 의 μˆœμ„œλŠ” @Order(n) 을 ν†΅ν•˜μ—¬ λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•˜μ§€ μ•ŠλŠ” 이상 등둝 μˆœμ„œλŠ” 보μž₯이 λ˜μ§€ μ•ŠλŠ”λ‹€.

Multi-SecurityFilterChain ν™˜κ²½

μ—¬λŸ¬ 개의 SecurityFilterChain 이 μ‘΄μž¬ν•˜λŠ” ν™˜κ²½μ—μ„œλŠ” μ–΄λ–€ κΈ°μ€€μœΌλ‘œ νŠΉμ • SecurityFilterChain 을 μ„ νƒν•˜μ—¬ request λ₯Ό μ „λ‹¬ν•˜λŠ” κ²ƒμΌκΉŒ? μš°μ„ μ€ λ‹€μŒ 기쀀을 λ”°λ₯Έλ‹€.

  1. λ“±λ‘λœ index 순
  2. Filter Chain 에 λŒ€ν•œ RequestMatcher κ°’μ˜ 일치 μ—¬λΆ€

RequestMatcher λŠ” λ“€μ–΄μ˜€λŠ” request 의 URL, HTTP Method, Header 등을 κΈ°μ€€μœΌλ‘œ matching ν•˜μ—¬, ν•΄λ‹Ή request μ—λ§Œ Security Rule 을 μ μš©ν•  수 있게 λ„μ™€μ£ΌλŠ” 데에 μ‚¬μš©λ˜λŠ” Interface 이닀.

RequestMatcher λŠ” 주둜 λ‹€μŒκ³Ό 같은 μƒν™©μ—μ„œ μ‚¬μš©ν•œλ‹€.

  1. securityMatcher: SecurityFilterChain 이 μ•„μ˜ˆ 적용될 request 의 경둜λ₯Ό μ§€μ •
  2. authorizeHttpRequests(): 이미 SecurityFilterChain 이 적용된 request λ“€ μ€‘μ—μ„œ, 각 κ²½λ‘œλ‚˜ κΆŒν•œ λ“±μ˜ μ‘°κ±΄λ³„λ‘œ Authorization κ·œμΉ™μ„ μ„ΈλΆ€μ μœΌλ‘œ μ§€μ •, 주둜 .requestMatchers() λ₯Ό μ‚¬μš©ν•˜μ—¬ 쑰건을 μ„€μ •
문제 상황
@Bean
public SecurityFilterChain adminChain(HttpSecurity http) throws Exception {  
    http  
            .authorizeHttpRequests((auth) -> auth  
                    .requestMatchers("/admin").permitAll());  
  
    return http.build();  
}  
  
@Bean  
public SecurityFilterChain publicChain(HttpSecurity http) throws Exception {  
    http  
            .authorizeHttpRequests((auth) -> auth  
                    .requestMatchers("/user").permitAll());  
  
    return http.build();  
}

μš°μ„  μœ„μ™€ 같이 두 개의 SecurityFilterChain 을 λ“±λ‘ν–ˆλ‹€κ³  κ°€μ •ν•΄λ³΄μž. 각각 requestMatchers() λ₯Ό μ‚¬μš©ν•˜μ—¬ ν•˜λ‚˜λŠ” /admin κ²½λ‘œμ—, λ‹€λ₯Έ ν•˜λ‚˜λŠ” /user κ²½λ‘œμ— λŒ€ν•œ request λ₯Ό μ²˜λ¦¬ν•˜λ„λ‘ λ˜μ–΄ μžˆλŠ” 것 κ°™λ‹€.

κ·ΈλŸ¬λ‚˜ securityMatcher λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ ν•΄λ‹Ή SecurityFilterChain 은 /**, 즉 λͺ¨λ“  request κ²½λ‘œμ— λŒ€ν•˜μ—¬ μ μš©λœλ‹€. μœ„μ™€ 같이 λͺ¨λ“  SecurityFilterChain 이 securityMatcher λ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  μžˆλ‹€λ©΄, FilterChainProxy κ°€ κ΄€λ¦¬ν•˜λŠ” SecurityFilterChain λ“€ 쀑, index κ°€ 더 μž‘μ€ SecurityFilterChain 이 μ„ νƒλœλ‹€. 더 μ‰½κ²Œ λ§ν•˜λ©΄ /admin 으둜 request λ₯Ό λ°›λ˜, /user 둜 request λ₯Ό λ°›λ˜ λͺ¨λ‘ index κ°€ 더 μž‘μ€ SecurityFilterChain 이 μ μš©λœλ‹€λŠ” λœ»μ΄λ‹€.

λ§Œμ•½ adminChain 이 index 0 에 λ“±λ‘λ˜μ–΄ μžˆλ‹€λ©΄, /user κ²½λ‘œμ— λŒ€ν•˜μ—¬ request λ₯Ό 보내도 adminChain 이 적용될 것이닀. ν•΄λ‹Ή SecurityChainFilter μ—λŠ” /user κ²½λ‘œμ— λŒ€ν•œ λ³„λ„μ˜ 처리 과정이 λͺ…μ‹œλ˜μ–΄ μžˆμ§€ μ•ŠμœΌλ―€λ‘œ /user request λŠ” λ¬΄μ‹œλ  것이닀.

λ‹€ν–‰νžˆ μ΅œμ‹  λ²„μ „μ˜ Spring Security(6.x) μ—μ„œλŠ” μ—¬λŸ¬ 개의 SecurityFilterChain λ₯Ό define ν•  λ•Œ, 각 Chain 이 μ–΄λ–€ request 에 μ μš©λ μ§€ λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄, 즉 securityMatcher λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠμœΌλ©΄ Application μ‹€ν–‰μ—μ„œ 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

ν•΄κ²°
@Bean  
public SecurityFilterChain adminChain(HttpSecurity http) throws Exception {  
    http  
            .securityMatcher("/admin")  
            .authorizeHttpRequests((auth) -> auth  
                    .requestMatchers("/admin").permitAll());  
  
    return http.build();  
}  
  
@Bean  
public SecurityFilterChain publicChain(HttpSecurity http) throws Exception {  
    http  
            .securityMatcher("/user")  
            .authorizeHttpRequests((auth) -> auth  
                    .requestMatchers("/user").permitAll());  
  
    return http.build();  
}

이 λ¬Έμ œμ— λŒ€ν•œ 해결은 κ°„λ‹¨ν•˜λ‹€. 각 SecurityFilterChain 에 λŒ€ν•˜μ—¬ securityMatcher λ₯Ό μ‚¬μš©ν•˜μ—¬ 적용될 request 의 경둜λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•˜λ©΄ λœλ‹€.

등둝 μˆœμ„œλ₯Ό 보μž₯ν•˜λŠ” 방법
@Bean
@Order(1)
public SecurityFilterChain adminChain(HttpSecurity http) throws Exception {  
	// ...
    return http.build();  
}  
  
@Bean
@Order(2)
public SecurityFilterChain publicChain(HttpSecurity http) throws Exception {  
	// ...
    return http.build();  
}

μ•žμ„œ μ—¬λŸ¬ 개의 SecurityFilterChain 을 λ“±λ‘ν•˜λ©΄ μˆœμ„œκ°€ 보μž₯λ˜μ§€ μ•ŠλŠ”λ‹€κ³  μ–ΈκΈ‰ν•˜μ˜€λ‹€. 이에 λŒ€ν•˜μ—¬ @Order(n) annotation 을 μ‚¬μš©ν•˜μ—¬ μˆœμ„œλ₯Ό μ§€μ •ν•  수 μžˆλ‹€.

μ˜λ„μ μœΌλ‘œ SecurityFilterChain 을 κ±°μΉ˜μ§€ μ•ŠλŠ” 방법

λ§Œμ•½ request κ²½λ‘œκ°€ 정적 μžμ›(image file, CSS, HTML λ“±)에 ν•΄λ‹Ήν•œλ‹€λ©΄ ꡳ이 λͺ¨λ“  SecurityFilterChain μ•ˆμ˜ SecurityFilter 듀을 κ±°μ³μ•Όν• κΉŒ?

μš°μ„  정적 μžμ›(Static Resources)듀은 κ³΅κ°œλ˜μ–΄μ•Ό ν•  file 듀이닀. λ”°λΌμ„œ κ±°μΉ  ν•„μš”κ°€ μ—†λ‹€.

λ˜ν•œ μ„±λŠ₯ μΈ‘λ©΄μ—μ„œ μœ λ¦¬ν•˜λ‹€. 정적 μžμ›λ“€μ€ κ·Έλƒ₯ μ„œλ²„μ—μ„œ 별도 처리 κ³Όμ • 없이 ν•΄λ‹Ή μžμ›λ§Œ response ν•΄μ£Όλ©΄ λœλ‹€. μ μ ˆν•œ SecurityFilterChain λ₯Ό μ„ νƒν•˜λŠ” 것에도 μ„œλ²„μ˜ μžμ›μ΄ 쓰이고, μ μ ˆν•œ SecurityFilterChain 을 찾은 후에도 λ‚΄λΆ€μ˜ λͺ¨λ“  SecurityFilter 듀을 κ±°μΉ˜λŠ” 것에도 μžμ›μ΄ 쓰인닀.

@Bean  
public WebSecurityCustomizer webSecurityCustomizer() {  
    return web -> web.ignoring().requestMatchers("/img/**");  
}

μœ„μ™€ 같이 WebSecurityCustomizer 에 λŒ€ν•œ Bean 을 SecurityConfig class 에 μΆ”κ°€ν•΄μ£Όλ©΄, 내뢀에 SecurityFilter κ°€ μ‘΄μž¬ν•˜μ§€ μ•Šμ€ μƒˆλ‘œμš΄ SecurityFilterChain 이 index 0 번으둜 λ“±λ‘λœλ‹€.

μ‹€μ œλ‘œ ν…ŒμŠ€νŠΈν•΄λ³΄λ©΄ μ •ν™•νžˆ 0 번 index 에 ν•΄λ‹Ή SecurityFilterChain 이 λ“±λ‘λœ 것을 λ³Ό 수 μžˆλ‹€.