2023. 12. 20. 00:52ㆍ도서/스프링부트와 AWS로 혼자 구현하는 웹 서비스
[작성이유]
예전에 교내 프로젝트를 했을 때에도 Spring Security를 이용했을 때에 Deprecated된 WebSecurityConfigurerAdapter를 사용해서 SecurityConfig를 작성을 했었다. 불과 2달 전까지만 해도 start.spring.io에 들어가면 Spring 2.7까지 지원을 했고 3.X이전 버전에서는 해당 함수가 검색이라도 되지만 3.X버전이 넘어가면 완전히 삭제돼서 사용자체도 불가능하다.
지난번에 넘긴 것도 찝찝했고 이번에는 spring 3.0 이상으로 하는 김에 WebSecurityConfigurerAdapter를 사용하지 않고 제대로 SecurityConfig를 작성하는 방법을 알아보았다.
[기본코드]
Security Config는 Spring Security의 보안 관련 설정 범위, 사용자 권한 같은 것을 정의하는 공간이다. 기존 책에 나와있는 코드를 우선 보자.
@Override
protected void configure(HttpSecurity http) throws Exception{
http.csrf().disable()
.headers().frameOptions().disable() // (1)
.and
.authorizeRequests()
.antMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**").permitAll()
.antMatchers("/api/v1/**").hasRole(Role.USER.name()) // (2)
.anyRequest().authenticated() // (3)
.and
.logout().logoutSuccessUrl("/") // (4)
.and
.oauth2Login().userInfoEndpoint().userService(customOAuth2UserService); // (5)
해당 코드를 간단하게 설명하자면 아래와 같다.
(1) h2-console 화면을 사용하기 위해 해당 옵션(csrf 방어 기능, 다른 웹페이지 프레임 로드)들을 disable 한다.
(2) 권한 별 설정이다. permitAll()은 권한과 관계 없이 가능한 곳, hasRole은 해당 권한을 가진 사람만 가능하다.
(3) 나머지에 대해서는 로그인한 사용자(인증된 사용자들)에게만 허용한다.
(4) Logout이 성공적으로 되는 경우 Redirect되는 주소를 의미한다.
(5) 소셜 로그인 성공 시 후속 조치를 취할 UserSerrvice 인터페이스의 구현체를 등록함으로써 사용자 정보를 가져온 상태에서 추가로 하고자 하는 기능을 명시하는 역할을 한다.
반드시 설정을 해놓아야하지만 안타깝게도 Spring 3.X 버전부터는 내용이 바뀌었다. 그것도 계속 바뀌어서 정말 헷갈린다. 나름대로 찾은 정답은 아래와 같다.
[변경코드]
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {
private final CustomeOAuth2UserService customOAuth2UserService;
private static final String[] PERMIT_ALL_PATTERNS = new String[]{
"/",
"/css/**",
"/image/**",
"/js/**"
};
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// h2-console 화면을 사용하기 위한 옵션
http
.csrf((csrf)-> csrf.disable())
.headers((headers) -> headers
.frameOptions(frameOptionsConfig -> frameOptionsConfig.disable()));
// URL 별 권한 관리를 설정하는 옵션
http.authorizeHttpRequests((authorize) -> authorize
.requestMatchers(PathRequest.toH2Console()).permitAll()
.requestMatchers(Stream.of(PERMIT_ALL_PATTERNS)
.map(AntPathRequestMatcher::antMatcher)
.toArray(AntPathRequestMatcher[]::new)).permitAll()
.requestMatchers(AntPathRequestMatcher.antMatcher("/api/v1/**")).hasRole(Role.USER.name())
.anyRequest().authenticated());
// Logout 성공시 주소 이동에 관한 설정
http.logout((logout)-> logout.logoutSuccessUrl("/"));
// Login 이후 사용자 정보를 가져올 떄의 설정 담당
http.oauth2Login((oauth2Login)-> oauth2Login
.userInfoEndpoint((userInfoEndpoint)->
userInfoEndpoint.userService(customOAuth2UserService)));
return http.build();
}
}
개인적으로 사용 코드를 보면 더 복잡해진 느낌으로 꽤나 큰 변화가 있다. 그리고 이전보다 사용법도 더 어려워진 것 같다고 생각한다.
첫 번째 큰 변화는 더이상 WebSecurityConfigurerAdapter를 상속받아서 작성하지 않는다. 이제는 @Configuration임을 정확하게 명시하고 SecurityFilterChain 빈을 등록해야 한다.
두 번째 큰 변화는 antMatcher를 사용하는 방법도 바뀌었고 옵션을 설정할 때에 람다와 스트림을 모르면 설정하기가 쉽지 않다. 이번에 바꾸면서 가장 이해가 안되었던 부분은 csrf().disable()이 체인으로 검색은 되는데 설정하고나면 빨간 불이 들어온 채로 그대로 있다. 그 이유는 설정 방식이 틀렸기 때문이었다. 이제 모든 내부 설정은 람다식으로 해주어야한다. 참고로 람다식 내부 이름은 마음대로 해도 된다. 대신 양식을 잘 지켜야 한다.
추가적으로 PERMIT_ALL_PATTERN은 한 줄 한 줄 전부 내부에 적는 것보다 가독성이 좋아보여서 한 블로그를 보고 따라 적어보았다.
세 번째 큰 변화는 더 이상 and로 연결하는 게 아닌 따로 http 설정을 문장 별로 해줄 수 있다는 점과 http를 작성하고 나면 SecurityFilterChain을 return 해야한다는 점이다. 이 부분은 솔직히 가독성적인 측면에서 정말 좋은 선택이라고 생각이 된다. 개인적으로 설정이 너무 길어지면 도대체 어디다가 주석을 두어야 할 지도 모르겠고 가독성이 많이 떨어졌었는데 이 부분은 정말 마음에 든다.
[변경사유]
Spring 블로그에 가보면 WebSecurityConfigurerAdapter없이 설정하기 부분이 있다. 여기에 가면 변경 사유를 적은 docs로 연결하는 링크가 하나 있는데 이번에 사용한 모든 코드와 유사한 예시와 변경 사유를 알려준다.
첫 번째 변경 사유는 기존의 방식은 Controller에서 한 번의 요청에 두 번의 승인이 발생하는 현상이 일어나는 문제가 발생했기 때문이라고 한다.
두 번째 변경 사유는 엔드포인트 승인 과정에서 우선 순위에 따라 Spring Security를 구성할 수 있다는 점 때문이라고 한다.
세 번째 변경된 점은 람다식을 사용하고 antMatcher부분을 RequesMatcher로 바꿈으로써 내부에 RegexRequestMatcher를 사용하면 정규식을 이용해서 정의할 수 있다. 정확하게 말하면 RequestMatcher 내부에서 antMatcher도 사용이 가능하지만 선택 폭이 더 넓어진 것 같다.
이 외에도 다양한 내용이 있지만 솔직히 Spring Security 내부 구조에 관한 전체적인 내용을 정확하게 모르니 이외의 변경된 내용들은 정확하게 이해가 되지 않는다. 아무튼 최대한 이해한 바로 Spring Security 6 이상이 적용된 Spring 3.X 버전에서 SecurityConfig 설정이었다. 도움이 되었기를 바란다.
[참고 링크]
https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter
https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html
Authorize HttpServletRequests :: Spring Security
While using a concrete AuthorizationManager is recommended, there are some cases where an expression is necessary, like with or with JSP Taglibs. For that reason, this section will focus on examples from those domains. Given that, let’s cover Spring Secu
docs.spring.io
Spring Security without the WebSecurityConfigurerAdapter
In Spring Security 5.7.0-M2 we deprecated the WebSecurityConfigurerAdapter, as we encourage users to move towards a component-based security configuration. To assist with the transition to this new style of configuration, we have compiled a list of common
spring.io