스프링 시큐리티 공식 레퍼런스를 한글로 번역한 문서입니다.
전체 목차는 여기에 있습니다.
목차:
- 16.1. Hello Web Security Java Configuration
- 16.2. HttpSecurity
- 16.3. Multiple HttpSecurity
- 16.4. Custom DSLs
- 16.5. Post Processing Configured Objects
스프링 프레임워크의 전반적인 자바 설정 지원은 스프링 3.1에서 추가됐다. 스프링 시큐리티는 3.2 버전부터 자바 설정을 지원하므로 XML 없이도 손쉽게 스프링 시큐리티 설정을 만들 수 있다.
시큐리티 네임스페이스 설정에 익숙하다면, 자바 설정도 네임스페이스 설정과 꽤 유사하다는 걸 알 수 있을 거다.
스프링 시큐리티는 자바 설정을 사용하는 많은 샘플 어플리케이션을 제공한다.
16.1. Hello Web Security Java Configuration
먼저 스프링 시큐리티 자바 설정을 만드는 것부터 시작한다. 이 설정은 springSecurityFilterChain으로 알려진, 어플리케이션 내 모든 보안 처리를 (어플리케이션 URL 보호, 제출한 사용자 이름과 비밀번호 검증, 로그인 폼으로 리다이렉트 등) 담당하는 서블릿 필터를 생성한다. 다음은 가장 기본적인 스프링 시큐리티 자바 설정 예시다:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;
@EnableWebSecurity
public class WebSecurityConfig {
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());
return manager;
}
}
이 설정은 간단하지만 많은 일을 해준다. 기능을 요약하면 다음과 같다:
- 어플리케이션의 모든 URL에 인증 요구
- 로그인 폼 생성
- 사용자 이름 user와 비밀번호 password를 사용한 사용자의 폼 기반 인증 지원
- 사용자 로그아웃 지원
- CSRF 공격 방어
- Session Fixation 방어
- 보안 헤더 통합
- HTTP Strict Transport Security로 요청을 보호
- X-Content-Type-Options 통합
- Cache Control (어플리케이션에서 특정 스태틱 리소스에 캐시를 허용하도록 재정의할 수 있다)
- X-XSS-Protection 통합
- X-Frame-Options 통합으로 클릭재킹 방어 지원
- 서블릿 API 메소드 통합
16.1.1. AbstractSecurityWebApplicationInitializer
다음 단계는 war에 springSecurityFilterChain을 등록하는 것이다. 자바 설정에선 서블릿 3.0+ 환경에서 지원하는 스프링의 WebApplicationInitializer를 사용할 수 있다. 놀랄것도 없이 당연히 스프링 시큐리티는 springSecurityFilterChain을 등록해주는 베이스 클래스 AbstractSecurityWebApplicationInitializer를 제공한다. AbstractSecurityWebApplicationInitializer를 사용하는 방법은 이미 스프링을 사용하고 있는지 또는 스프링 시큐리티가 어플리케이션의 유일한 스프링 컴포넌트인지에 따라 다르다.
- 기존 스프링 없이 AbstractSecurityWebApplicationInitializer 사용하기 - 스프링을 사용하고 있지 않다면 이 가이드를 따라라.
- 스프링 MVC에서 AbstractSecurityWebApplicationInitializer 사용하기 - 이미 스프링을 사용하고 있다면 이 가이드를 따라라.
16.1.2. AbstractSecurityWebApplicationInitializer without Existing Spring
스프링이나 스프링 MVC를 사용하고 있지 않다면, WebSecurityConfig를 사용할 수 있도록 이 클래스를 상위 클래스로 넘겨야 한다. 예시는 아래에 있다:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(WebSecurityConfig.class);
}
}
SecurityWebApplicationInitializer는 다음과 같은 일을 한다:
- 어플리케이션의 모든 URL에 자동으로 springSecurityFilterChain 필터를 등록한다
- WebSecurityConfig를 로드하는 ContextLoaderListener를 추가한다.
16.1.3. AbstractSecurityWebApplicationInitializer with Spring MVC
이미 다른 곳에서 스프링을 사용하고 있다면 아마 스프링 설정을 로딩하는 WebApplicationInitializer가 있을 거다. 이전 설정을 그대로 사용하면 오류가 생긴다. 대신에 기존 ApplicationContext에 스프링 시큐리티를 등록해야 한다. 예를 들어 스프링 MVC를 사용하고 있다면 SecurityWebApplicationInitializer는 다음과 같을 것이다:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
이 코드는 단순히 모든 어플리케이션 URL에 springSecurityFilterChain 필터를 등록한다. 이렇게 하면 기존 ApplicationInitializer에 WebSecurityConfig를 로드한다. 예를 들어 스프링 MVC를 사용하고 있다면 getRootConfigClasses() 안에 추가된다.
public class MvcWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { WebSecurityConfig.class };
}
// ... other overrides ...
}
16.2. HttpSecurity
지금까지 WebSecurityConfig에는 사용자를 인증하는 방법에 대한 정보만 있었다. 스프링 시큐리티는 모든 사용자를 인증해야 한다는 걸 어떻게 알 수 있을까? 폼 기반 인증을 지원해야 한다는 것은 또 어떻게 알까? 사실은 뒷단에서 실행하는 WebSecurityConfigurerAdapter라는 설정 클래스가 있다. 이 클래스엔 디폴트로 아래와 같이 구현돼 있는 configure라는 메소드가 있다:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
}
이 디폴트 설정은:
- 어플리케이션의 모든 요청은 인증한 사용자만 사용할 수 있음을 보장한다.
- 폼 기반 로그인 인증을 지원한다.
- HTTP 기본 인증을 지원한다.
이 설정은 XML 네임스페이스 설정과도 매우 유사하다는 것을 알 수 있다:
<http>
<intercept-url pattern="/**" access="authenticated"/>
<form-login />
<http-basic />
</http>
16.3. Multiple HttpSecurity
<http> 블록을 여러 개 만들 수 있듯이, HttpSecurity 인스턴스도 여러 개 설정할 수 있다. 핵심은 WebSecurityConfigurerAdapter를 여러 번 상속하는 것이다. 예를 들어 다음 예제에선 /api/로 시작하는 URL에선 다른 설정을 사용한다:
@EnableWebSecurity
public class MultiHttpSecurityConfig {
@Bean // (1)
public UserDetailsService userDetailsService() throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(users.username("user").password("password").roles("USER").build());
manager.createUser(users.username("admin").password("password").roles("USER","ADMIN").build());
return manager;
}
@Configuration
@Order(1) // (2)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") // (3)
.authorizeRequests(authorize -> authorize
.anyRequest().hasRole("ADMIN")
)
.httpBasic(withDefaults());
}
}
@Configuration // (4)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.formLogin(withDefaults());
}
}
}
(1) 평소처럼 인증을 설정한다.
(2) WebSecurityConfigurerAdapter 인스턴스를 생성하고, @Order를 명시해서 가장 우선시할 WebSecurityConfigurerAdapter로 설정한다.
(3) http.antMatcher는 이 HttpSecurity는 /api/로 시작하는 URL에만 적용한다고 말하고 있다.
(4) 다른 WebSecurityConfigurerAdapter 인스턴스를 생성한다. /api/로 시작하지 않는 URL은 이 설정을 사용한다. 이 설정은 1보다 큰 @Order 값을 가지므로 ApiWebSecurityConfigurationAdapter 이후에 사용한다 (기본적으로 @Order를 생략하면 마지막이 된다).
16.4. Custom DSLs
스프링 시큐리티에 직접 만든 커스텀 DSL을 제공할 수도 있다. 예를 들어 다음과 같은 코드가 있을 수 있다:
public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
private boolean flag;
@Override
public void init(H http) throws Exception {
// any method that adds another configurer
// must be done in the init method
http.csrf().disable();
}
@Override
public void configure(H http) throws Exception {
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
// here we lookup from the ApplicationContext. You can also just create a new instance.
MyFilter myFilter = context.getBean(MyFilter.class);
myFilter.setFlag(flag);
http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
}
public MyCustomDsl flag(boolean value) {
this.flag = value;
return this;
}
public static MyCustomDsl customDsl() {
return new MyCustomDsl();
}
}
실제로
HttpSecurity.authorizeRequests()같은 메소드도 동일한 방식으로 구현하고 있다.
이 커스텀 DSL은 다음과 같이 사용할 수 있다:
@EnableWebSecurity
public class Config extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.apply(customDsl())
.flag(true)
.and()
...;
}
}
코드 실행 순서는 다음과 같다:
Config에 있는 configure 메소드를 실행한다.MyCustomDsl에 있는 init 메소드를 실행한다.MyCustomDsl에 있는 configure 메소드를 실행한다.
원한다면 SpringFactories를 사용해서, WebSecurityConfiguerAdapter가 기본적으로 MyCustomDsl을 추가하도록 만들 수 있다. 예를 들어 아래와 같은 내용을 담은 META-INF/spring.factories 리소스를 클래스패스에 만들 수 있다:
META-INF/spring.factories
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl
디폴트 설정을 명시적으로 비활성화할 수도 있다.
@EnableWebSecurity
public class Config extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.apply(customDsl()).disable()
...;
}
}
16.5. Post Processing Configured Objects
스프링 시큐리티의 자바 설정은, 설정하는 모든 객체의 전체 프로퍼티를 노출하지는 않는다. 이로써 대다수에게는 설정을 단순화해준다. 어쨋든, 모든 프로퍼티를 노출했다면 사용자는 표준 빈 설정을 사용했을 것이다.
모든 프로퍼티를 직접 노출하지 않는 데에는 그만한 이유가 있지만, 좀 더 세세하게 설정을 변경하고 싶을 수도 있다. 이를 해결하기 위해 스프링 시큐리티는 자바 설정으로 만든 인스턴스를 수정하거나 대체할 수 있는 ObjectPostProcessor 개념을 도입했다. 예를 들어 FilterSecurityInterceptor의 filterSecurityPublishAuthorizationSuccess 프로퍼티를 설정하려는 경우 다음 코드를 사용할 수 있다:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setPublishAuthorizationSuccess(true);
return fsi;
}
})
);
}
Next :
Kotlin Configuration
코틀린 코드로 스프링 시큐리티를 설정하는 방법을 설명합니다. 공식 문서에 있는 "Kotlin Configuration" 챕터를 한글로 번역한 문서입니다.
전체 목차는 여기에 있습니다.