경험/이슈

Spring Security 버전 이슈로 인한 오류 해결

호야_ 2023. 8. 30. 22:35
728x90

먼저 이 문제는 '스프링 부트와 AWS로 혼자 구현하는 웹 서비스'라는 책을 실습하는 과정에서 마주친 상황이다.

책은 몇 년 전에 지어진 기준이라 자바나 스프링부트의 버전이 낮게 되어있다. 그렇지만 그 당시 버전보다는 지금 상황에서 안정된 최신 버전으로 변경사항은 고치며 해보고 싶어 최신버전으로 진행했다.

  • Spring Boot : 3.1.2
  • Java : 17
  • Gradle : 8.2.1

spring-security-config : 6.1.2

 

우선 책에서는

@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final CustomOAuth2UserService customOAuth2UserService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .headers().frameOptions().disable()
                .and()
                    .authorizeRequests()
                    .antMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**", "/profile").permitAll()
                    .antMatchers("/api/v1/**").hasRole(Role.USER.name())
                    .anyRequest().authenticated()
                .and()
                    .logout()
                        .logoutSuccessUrl("/")
                .and()
                    .oauth2Login()
                        .userInfoEndpoint()
                            .userService(customOAuth2UserService);
    }
}

이렇게 구성되어 있었다.

 

Deprecated List (spring-security-docs 6.1.3 API)

Authorize HttpServletRequests :: Spring Security

공식문서를 참고해서 deprecated된 것을 알맞게 나름 수정해 봤다.

 

@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig {

    private final CustomOAuth2UserService customOAuth2UserService;

    @Bean
    protected SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http
                .csrf(AbstractHttpConfigurer::disable)

                .headers((headers) -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))

                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**", "/profile").permitAll()
                        .requestMatchers("/api/v1/**").hasRole(Role.USER.name())
                        .anyRequest().authenticated())
                .logout((logout) -> logout
                        .logoutSuccessUrl("/"))

                .oauth2Login((oauth2) -> oauth2
                        .userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint
                                .userService(customOAuth2UserService)))

        return http.build();
    }
}

 

문제 상황

우선 security가 제대로 작동하지 않는 것 같았다. `permitAll()`을 사용해서 지정된 경로를 들어갔을 때는 인증을 요구하지 않게 했는데도 자꾸 로그인으로 자동으로 redirect 됐다. 그래서 공식문제 예제들을 보니 `@Configuration`이 있다는 것을 확인했다. (책의 예제는 왜 없는지 모르겠다.. )

 

`@Configuration`을 사용하니 아예 에러를 내보냈는데

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Unsatisfied dependency expressed through method 'setFilterChains' parameter 0: Error creating bean with name 'configure' defined in class path resource [com/hoya/studying/springbootwebservice/config/auth/SecurityConfig.class]: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'configure' threw exception with message: This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).

Spring Security 6.1.2 String requestMatchers error UnsupportedOperationException · Issue #13609 · spring-projects/spring-security

이 글을 참고하면 해결할 수 있다.

 

해결

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final CustomOAuth2UserService customOAuth2UserService;

    @Bean
    protected SecurityFilterChain configure(HttpSecurity http) throws Exception {
        http
                .csrf(AbstractHttpConfigurer::disable)

                .headers((headers) -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))

                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(
                                new AntPathRequestMatcher("/"),
                                new AntPathRequestMatcher("/css/**"),
                                new AntPathRequestMatcher("/images/**"),
                                new AntPathRequestMatcher("/js/**"),
                                new AntPathRequestMatcher("/h2-console/**"),
                                new AntPathRequestMatcher("/profile")
                        ).permitAll()
                        .requestMatchers(new AntPathRequestMatcher("/api/v1/**")).hasRole(Role.USER.name())
                        .anyRequest().authenticated())

                .logout((logout) -> logout
                        .logoutSuccessUrl("/"))

                .oauth2Login((oauth2) -> oauth2
                        .userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint
                                .userService(customOAuth2UserService))
                        .defaultSuccessUrl("/", true));

        return http.build();
    }
}
서버를 시작하고 맨 처음에 로그인을 성공하면 자체적으로 설정한 파비콘 링크로 리다이렉트가 돼서 defaultSuccessUrl을 true로 해서 무조건 "/"로 가게 설정했다.

 

 

 

언제나 잘못된 설명이나 부족한 부분에 대한 피드백은 환영입니다🤍

728x90