안녕하세요. 유저인사이트 박태양입니다.
저희는 신규 프로젝트를 시작할때, SecurityConfig라는 클래스 안에 여러가지 보안 관련 코드를 구현합니다.
초기에 잘 작성된 코드이기에 특별한 수정 없이 사용하였지만,
간혹 해당 프로젝트에는 필요없는 코드도 섞여들어가는 경우도 있었습니다.
Spring Security 버전이 변경되면서, 이번 기회에 보안 관련 코드를 정리하는 시간을 가져보았습니다.
목차
- WebSecurityConfigureAdapter (deprecated)
- WebSecurity vs HttpSecurity
- HttpSecurity의 다양한 옵션들
1. WebSecurityConfigureAdapter (deprecated)
기존에는 WebSecurityConfigureAdapter를 상속받아 보안 관련 클래스를 구성하였습니다.
하지만, Spring Security 5.7.0-M2 부터 해당 클래스가 deprecated 되었기 때문에 새로운 방식이 필요합니다.
we encourage users to move towards a component-based security configuration.
스프링 공식 블로그에 나와있는 안내문입니다. 컴포넌트 기반으로 시큐리티를 설정하도록 유도하고 있습니다.
아래 코드처럼 SecurityFilterChain을 빈으로 등록하여주면 됩니다.
기존코드
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
}
}
개선된코드 (SecurityFilterChain 빈 등록)
@Configuration
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.httpBasic(withDefaults());
return http.build();
}
}
2. WebSecurity vs HttpSecurity
WebSecurity는 일반적으로 ignoring() 처리를 통해서
Spring Security에서 인증받지 않아도 되는 패턴이나 URL을 정의할때 사용합니다. (리소스 등)
반면에, HttpSecurity는 특정 URL에서 보안 인증을 받도록 설정할 수 있습니다.
기존코드
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
개선된코드
@Configuration
public class SecurityConfiguration {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
}
}
3. HttpSecurity의 다양한 옵션들
HttpSecurity 내에는 다양한 옵션들이 존재합니다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/attachment/**").permitAll()
.antMatchers("/auth/**").anonymous()
.anyRequest().authenticated();
return http.build();
}
authorizeRequests는 URL 패턴을 통해 액세스를 제한할 수 있게 해줍니다.
/attachment/** 는 누구에게나 허용하고, /auth/** 는 익명 사용자에게만 허용합니다.
나머지 요청은 모두 인증을 받은 상태여야 접근이 가능합니다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.formLogin().loginPage(SpringSecurity.LOGIN_URL)
.loginProcessingUrl(SpringSecurity.LOGIN_PROCESS_URL)
.successHandler(customAuthenticationSuccessHandler())
.failureHandler(customAuthenticationFailureHandler())
.usernameParameter(SpringSecurity.PARAM_USERNAME)
.passwordParameter(SpringSecurity.PARAM_PASSWORD);
return http.build();
}
formLogin은 폼 기반 인증을 사용할 수 있도록 합니다.
파라미터로 들어가는 URL은 SpringSecurity 클래스에 상수로 지정해놓고 사용하였습니다.
로그인 성공 및 실패 시 제어를 위해 CustomAuthenticationHandler를 구현하였습니다.
@Bean
public AuthenticationFailureHandler customAuthenticationFailureHandler() {
return new CustomAuthenticationHandler();
}
@Bean
public AuthenticationSuccessHandler customAuthenticationSuccessHandler() {
return new CustomAuthenticationHandler();
}
위 폼 로그인 설정에 사용되는 파일들을 빈으로 등록하였습니다.
CustomAuthenticationHandler는 AuthenticationSuccessHandler, AuthenticationFailureHandler를 implments 하였습니다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.logout()
.logoutRequestMatcher(new AntPathRequestMatcher(SpringSecurity.LOGOUT_URL))
.logoutSuccessUrl(SpringSecurity.LOGIN_URL)
return http.build();
}
로그아웃 요청 URL과 성공 시 이동하게 될 URL을 지정합니다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.exceptionHandling()
.accessDeniedPage(SpringSecurity.LOGIN_SUCCESS_URL)
.authenticationEntryPoint(ajaxAwareAuthenticationEntryPoint());
return http.build();
}
접근 거절 시 이동하게 될 URL을 설정합니다.
@Bean
public LoginUrlAuthenticationEntryPoint ajaxAwareAuthenticationEntryPoint() {
return new AjaxAwareAuthenticationEntryPoint(SpringSecurity.LOGIN_URL);
}
ajaxAwareAuthenticationEntryPoint는 세션이 만료된 상태에서 ajax 요청을 하지 못하도록 설정해주는 클래스입니다.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.sessionManagement().maximumSessions(1)
.maxSessionsPreventsLogin(false)
.expiredUrl(SpringSecurity.LOGIN_URL).and()
.invalidSessionUrl(SpringSecurity.LOGIN_URL);
return http.build();
}
세션 최대 갯수를 1개로 설정합니다.
새로운 사용자가 로그인을 한 경우 기존 사용자의 세션을 만료시키고 로그인 페이지로 돌려보냅니다.
이상으로 Spring Security 관련 Configuration을 알아보았습니다.
새로운 프로젝트를 시작하거나, 기존 프로젝트의 보안 관련 부분을 갱신할때 도움이 되었으면 좋겠습니다.
감사합니다.
참고자료 1 : https://spring.io/guides/gs/securing-web/
참고자료 2 : https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
'세미나' 카테고리의 다른 글
Github Actions을 통한 Docker이미지 자동 배포 (0) | 2023.04.27 |
---|---|
Svelte로 웹 개발을 간소화하기 (0) | 2023.04.13 |
GPS 데이터와 좌표계에 대한 문제 해결 과정 (0) | 2023.03.17 |
Spring Boot 프로젝트에 Swagger 적용하기 (+인증) (1) | 2023.03.07 |
ChatGPT 체험 후기 (0) | 2023.02.24 |