Spring Security Redirect 로직 우회

Rookedsysc·2024년 2월 19일
0

발단 🦶

인자를 RequestParam으로 받아오면서 PathVariable로 요청하고 있는 멍청한 짓을 했다. 당연히 Controller에서 에러가 터졌고 ExceptionHandler 중에서 Controller 단의 에러를 핸들링하고 있는 로직이 없어서 Security 기본 ExceptionHandler에서 "머여? 에러났셔?@.@ 그럼 로그인 일단 한 번 해봐~@.@" 라며 리다이렉트 해주니...

나는 처음에는

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String token = resolveToken(request);

        if (StringUtils.hasText(token) && tokenProvider.validateToken(token)) {
            Authentication authentication = tokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);
    }

이 부분에서 Oauth의 인증로직은 건너뛰지 못하는건가? 아예 다른 패키지인가? 하고 있었는데 그게 아니었다. 그냥 애시당초 저걸로 인증 다 건너뛰고나서 에러가 터져버리니 Redirect 시켜준 것이었다. (여기서 한 1~2시간 삽질함)

👇 아래는 로그인 페이지로 리다이렉트 시켜주는 결과창

문제해결 과정

꼼수부리기

Spring Security Config에서

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(req -> {
                    // static Resource에 대해서는 모두 허가함
                    req.requestMatchers(
                                    PathRequest.toStaticResources()
                                            .atCommonLocations()
                            )
                            .permitAll()
                            .requestMatchers(it -> isJwtAuthenticatedRequest(it))
                            .permitAll()
                            ...
                      
private boolean isJwtAuthenticatedRequest(HttpServletRequest request) {
        String token = jwtAuthorizationFilter.resolveToken(request); // Reuse if already have this method
        if (StringUtils.hasText(token) && tokenHelper.validateToken(token)) {
            return true;
        }
        return false;
    }

token을 인증해버려서 그냥 건너뛰어버리는 방법으로 구현했다. 근데 이래도 에러가 나는 것이다! 위에도 기술했듯이 애시당초 인증로직의 문제가 아니었다...

정상적인 요청을 보내기

일단은

@GetMapping("/lol/{keyword}")
    public List<RedisCachedGame> searchLol(
            @RequestParam
            String keyword) {
        return searchService.cachedSearch(keyword);
    }

요 부분을

@GetMapping("/lol/{keyword}")
    public List<RedisCachedGame> searchLol(
            @PathVariable
            String keyword) {
        return searchService.cachedSearch(keyword);
    }

이렇게 바꿔주고나서 정상적으로 응답하는 것을 확인했다.

근데 이러고 나니 애시당초 저 redirect 로직 자체를 없애고 그냥 인증이 안됐어서 잘못된 요청이라는 응답을 보낼 방법은 없나? 라는 생각이 들었다.

Redirect 대신 비인가 접근 에러코드 리턴

DefaultAuthenticationEntryPoint 클래스를 상속받는 새 AuthenticationEntryPoint 클래스를 만들어서 Commence()를 오버라이드해서 에러를 반환하도록 구현하는 것이다.

public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }
}

그리고 SpringSecurityConfig에 CustomEntryPoint를 등록해준다.

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

        /**
         * Exception 발생시 Redirect를 하지 않고 401을 반환
         */
        http.exceptionHandling(
                exceptionHandlingConfigurer -> exceptionHandlingConfigurer.authenticationEntryPoint(new MyAuthenticationEntryPoint())
        );

이제 아까와 동일한 에러를 발생시켜도

정상적으로 에러응답이 오는 것을 확인할 수 있다.

0개의 댓글