SpringSecurity - 실패한 DSL(javaconfig)

2 minute read

원래 스프링시큐리티는 구조가 단순하다.

Filter가 하나 등록된다

public class FilterChainProxy extends GenericFilterBean

public class DefaultFilterChainValidator implements FilterChainProxy.FilterChainValidator

public interface SecurityFilterChain

ChainOfResponsibility패턴으로 구현된 FilterChain….

그리고 이 Chain에서 순차적으로 Filter를 실행시킨다.으로 인증필터 몇개 넣어준 다음
Request들어올 때 마다 반복문 돌려서 주소패턴 match되면 처리해주는거

  1. WebAsyncManagerIntegrationFilter
  2. SecurityContextPersistenceFilter
  3. HeaderWriterFilter
  4. CsrfFilter
  5. LogoutFilter
  6. UsernamePasswordAuthenticationFilter
  7. DefaultLoginPageGeneratingFilter
  8. DefaultLogoutPageGeneratingFilter
  9. BasicAuthenticationFilter
  10. RequestCacheAwareFilter
  11. SecurityContextHolderAwareRequestFilter
  12. AnonymousAuthenticationFilter
  13. SessionManagementFilter
  14. ExceptionTranslationFilter
  15. FilterSecurityInterceptor
https://www.javadevjournal.com/spring-security/spring-security-filters/
https://docs.spring.io/spring-security/reference/6.0.0-M3/servlet/architecture.html#servlet-filterchainproxy

대강 앞에서 막아줄거 막고 -> Authentication -> Principal threadlocal에 쑤셔박기 -> 꺼내서 Authorization하는 구조

좀 복잡한 필터가 몇개 있는데 사실 간단하게 구현해도 되는 부분이 많다.
UsernamePasswordAuthenticationFilter 등등 몇가지에서 너무 많은걸 자동으로 해주려다 보니 과도하게 복잡해진게 있긴한데.. 그래도 쓸만했었다

DSL이 나오기 전까지는…

프레임워크 자체도 조금 개선이 필요하다고 생각하는데
(UserDetails에 기본User를 생성해놓는다던가…하는 구현체를 너무 많이 만들어놓는것)
그래도 꽤 좋았는데

Java Config DSL에다가 Springboot Autoconfiguration까지 들어가니까…

또 스프링에서 자동으로 해주겠다고 하다가 망한 케이스

bean을 하나하나 설정 할 때는 혼란이 없었다.

설정 해 놓은대로
순차적으로 설정된 필터가 몇개인지
어떤게 설정되어 있는지 보였으니까
오류가 발생하면 어디서 발생했는지 이름만 봐도 바로 알 수 있었다.

그런데 JavaConfig는?

이게 몇년전쯤 쓰던 SpringSecurity 자바설정파일인데 이걸 보고 뭐가먼저 동작할지 예상이나 할 수 있을까


@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
	http
			.csrf().disable()
//좆같은거 추가
		.anonymous().and().anonymous().and().csrf().and()
			.authorizeRequests()
			//resource
			.antMatchers("/favicon.ico", "/vendor/**", "/static/**").permitAll()
			.antMatchers("/", "/home", "/join").permitAll()
			.antMatchers("/console/*").permitAll()
			.antMatchers("/user/join").anonymous()
			.anyRequest().authenticated()
			.and()

			.headers()
			.addHeaderWriter(new StaticHeadersWriter("X-Content-Security-Policy", "script-src 'self'"))
			.frameOptions().disable()
			.and()

			.formLogin()
			.loginPage("/login")
			.permitAll()

			.and()

			.logout()
			.permitAll();
}

	@Bean
	public DbUserDetailsService userDetailsService() {
		return new DbUserDetailsService();
	}

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

	@Bean
	public DaoAuthenticationProvider authenticationProvider() {
		DaoAuthenticationProvider bean = new DaoAuthenticationProvider();
		bean.setUserDetailsService(userDetailsService());
		bean.setPasswordEncoder(passwordEncoder());
		return bean;
	}

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
		auth
				.authenticationProvider(authenticationProvider());
//				메모리 로그인으로 적용시 사용.
//				.inMemoryAuthentication()
//				.withUser("user").password("password").roles("USER");
	}
}

내 코드에서 이정도 설정을 했지만
WebSecurityConfigurerAdapter에서 또 뭔가 존나게 해놨다

이 설정을 보면 /login이 로그인 페이지로 간다는건 대강 눈치챌 수 있지만 몇번째 필터에서 돌아가는지 알 수 있을까? 문제가 발생하면 디버깅이나 로그를 존나 훑어봐야알 수 있게 된다.
스프링 시큐리티에서는 이런식으로 문제가 발생한다.

XML 도 마찬가지

갑자기 생각 해 보니 java config가 개판이 된게 XML설정을 JavaConfig화 해서 그랬던 것 같다. 진짜 문제는 javaconfig가 아닌 설정방법 그 자체였던듯

이런식으로 설정하는건데…
그냥 튜토리얼 쓸때는 괜찮은데 커스텀 하기 시작하면 bean을 하나하나 하는게 낫다.

<?xml version="1.0" encoding="UTF-8"?> 
<beans:beans xmlns="http://www.springframework.org/schema/security" 
xmlns:beans="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans.xsd 
http://www.springframework.org/schema/security 
http://www.springframework.org/schema/security/spring-security.xsd"> 
<http auto-config="true"> 
<intercept-url pattern="/admin"
access="hasRole('ROLE_ADMIN')" /> </http> 
<authentication-manager> 
<authentication-provider>
~~~

결론?

스프링시큐리티는 수동으로 하나하나 설정해서 쓰자
어차피 한번 만들어놓으면 계속 쓰니까
이미 JavaConfig(DSL)로 선언 해 놓은것들도 다 하나하나 생성하는 방식으로 만들어야된다고 생각한다.

(간단하게 쓰려면 그냥 DSL을 써도 괜찮음)

https://docs.spring.io/spring-security/reference/6.0.0-M3/servlet/architecture.html#servlet-filterchainproxy