Java/Spring

[Spring Security] permitAll() 적용 안되는 문제

LimeCoding 2024. 1. 10. 18:58

문제 상황


 

이번에 프로젝트를 진행하며넛 스프링 시큐리티를 이용하여 로그인을 하는 기능을 구현할 일이 있었다.

진행 과정 중에 permitAll()을 이용해서 접근 권한을 설정하는 부분에서 계속 무한히 login 페이지로 리다이렉팅하는 문제가 생겼다.

문제가 생긴 코드는 다음과 같다.

package com.tutormatching.dotommorow.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;


@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final String[] whiteList = {"/", "/user/join", "/css/**", "/images/**", "/js/**",
            "/h2-console/**"};

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(request -> request
                        .requestMatchers(whiteList).permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(login -> login.loginPage("/login")
                permitAll()
                        .defaultSuccessUrl("/",true)
                        .permitAll()
                )
                .logout(logout -> logout
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/")
                );

        return http.build();
    }

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

 

 

문제 원인 파악 분석


white list에 있는 경로로 접근할 때는 권한에 상관없이 접근할 수 있도록 설정하고 싶었다. 그래서 위와 같이 설정을 했는데 무한히 login 페이지로 접속하는 문제가 발생하였다.

에러 로그가 길어서 중간만 캡쳐를 했다.

 

위와 같은 작동을 반복했는데 반복의 내용을 보면 필터를 돌면서 요청과 매칭이 되는지를 검사하고 있다. 이때 검사하는 대상이 "/"와 "/WEB-INF/views/index.jsp"이다. 코드 흐름을 보면 다음과 같다.

  1. "/"로 요청이 들어온다
  2. "/"에 대한 접근 권한을 검사한다
  3. index.jsp를 반환하는 함수를 실행한다.
  4. "/WEB-INF/views/index.jsp"로 요청이 들어온다
  5. "/WEB-INF/views/index.jsp"에 대한 접근은 permitAll이 아니므로 
    로그인 페이지로 넘어간다.
  6.  리다이렉트로 로그인 페이지에 대한 접근 권한을 요청한다
  7. 로그인 페이지에 대한 접근 권한은 permitAll이 아니므로 
    로그인 페이지에 대한 접근을 위해 로그인 페이지를 요청한다
  8. 6번 무한 반복

해결 방법


이 문제를 해결하기 위해 "/WEB-INF/views/index.jsp"를 whitelist에 추가하여 접근 권한을 변경해주었다. 결과적으로는 접근에 성공하였다. permitAll()이 적용이 안되는 게 아니라 설정을 올바르게 하지 못한 거 였다.

package com.tutormatching.dotommorow.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;


@Configuration
@EnableWebSecurity
public class SecurityConfig {

	// 수정된 부분
    private final String[] whiteList = {"/", "/user/join", "/css/**", "/images/**", "/js/**",
            "/h2-console/**", "/error/**", "/WEB-INF/views/**"};

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(request -> request
                        .requestMatchers(whiteList).permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(login -> login.permitAll()
                        .defaultSuccessUrl("/",true)
                        .permitAll()
                )
                .logout(logout -> logout
                        .logoutUrl("/logout")
                        .logoutSuccessUrl("/")
                );

        return http.build();
    }

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

 

결론


 이 문제를 해결하기 위해 2시간 이상 쓴 것 같다. 문제가 발생한 이유를 빨리 찾지 못한 이유는 첫 번째로 로그를 잘 읽지 않았다. 조금만 천천히 로그를 읽었다면 충분히 문제를 파악할 수 있었을 것이다. 두 번째 이유는 내부에서 돌아다니는 요청은 필터에 걸리지 않을 것이라고 생각했다. 스프링 시큐리티가 어느 범위까지 필터가 적용되는지 정확하게 파악하지는 못했다. 그러나 문제가 발생할 당시에는 외부에서 오는 url 요청만 접근 통제를 하고 내부에 대한 접근 통제는 안한다고 생각했다. 이 문제를 해결한다고 ignoring도 사용해봤는데 무한 루프는 똑같았을 뿐만 아니라 ignoring을 통한 접근 제어는 어떤 보호도 받지 못하기 때문에 위험하다는 경고와 함께 permitAll을 이용하라는 메세지를 봤다. 메세지가 잘 알려주어 결국 답을 찾기는 했지만 만약 디버깅을 할 수 있었다면 더 빨리 찾지 않았을까 싶다.

 현재 mybatis와 spring data jpa, querydsl 등을 배우고 있어 스프링 시큐리티를 당장 깊게 파지는 못하겠지만 공부가 끝나는 대로 스프링 시큐리티도 차근차근 배워나갈 생각이다.