[Spring Security] 스프링 시큐리티 - java.lang.StackOverflowError 발생

leeng·2023년 4월 2일
0

SpringSecurity

목록 보기
2/2

역시 또 사소한 오류로 몇 시간 삽질을 했다...ㅠㅠ
바로 JWT 검증 필터인 JwtAuthenticationFilter에서 StackOverflow가 자꾸 발생하는 것이었다.

아예 Authorization 헤더가 없거나, JWT 검증에 실패하거나 하는 경우에는 내가 설정한 대로 예외를 던지고 있는데 JWT 검증에 성공한 경우에는 어김없이 StackOverflow가 발생했다.


일단 Spring Security를 적용하면서 생긴 수정사항을 살펴보자.
기존에는 JwtAuthenticationFilter 클래스가 OncePerRequestFilter를 상속받던 것에서 BasicAuthenticationFilter를 상속받도록 수정하였다.

그리고 JWT가 유효한 것이어서 Decoded된 JWT에서 sub 클레임(사용자 id) 값을 정상적으로 꺼내고 나면, 기존에는 session에 회원정보를 담는 것에서 Authentication 객체에 id 값을 넣은 후 이 객체를 SecurityContextHolder에 세팅하는 로직으로 변경하였다.

별 문제 없이 무난하게 수정한 것 같은데....

심지어 기존에 다른 프로젝트에서 사용했던 스프링 시큐리티 필터를 참고해서 변경한 것이었다. 그런데 다른 프로젝트에서는 잘 동작하던게 갑자기 무한루프에 빠지다니.....

열심히 ChatGPT와 구글링을 하며 몇 시간의 삽질을 했더랬다.
AOP Proxy가 문제인가 싶어 잘 알지도 못하는 설정을 이것저것 해보고 Spring Security에서 AuthenticationManager 주입하는 부분이 잘못됐나 해서 시큐리티 설정도 이것저것 바꿔보고 ㅠㅠ

결론은 내가 처음에 new UsernamePasswordAuthenticationToken를 생성할 때, '여기서 권한은 사용 안하고 인증만 사용해야지'하고서 인자 2개짜리 생성자를 사용한 것이 화근이었다.



UsernamePasswordAuthenticationToken을 생성할 때 인자를 2개만 넣으면 아래와 같은 메시지가 뜬다.

이걸 사용하면 isAuthenticated()를 false를 반환한다는 의미이다.

아니 isAuthenticated를 false로 반환한다고 쳐도 그럼 그냥 401이나 403 에러가 발생해야지 왜 무한루프에 빠지는 건데???

또 다른 문제는 저 필터가 BasicAuthenticationFilter를 상속하고 있다는 것이었다.

BasicAuthenticationFilter에서 SecurityContextHolder의 Authentication 객체가 null인 경우, 요청에 대한 인증이 필요한 것으로 간주되어 다시 인증을 요구하고, 이 인증 요구가 다시 JwtAuthenticationFilter로 리다이렉트되어 무한 루프가 발생하는 것 같다

는 것이 ChatGPT의 의견이었다.(filter에서 stackoverflow가 발생하는 경우가 많지 않은지 구글링을 해봐도 크게 쓸만한 정보를 얻을 수가 없었다.)


어쨌든 원인을 찾았으니 UsernamePasswordAuthenticationToken 생성자에 세번째 인자를 넣어보자.
세번째 인자(권한)로 빈 리스트를 주었더니 잘 동작하는 것을 확인할 수 있다.

new UsernamePasswordAuthenticationToken(id, null, Collections.emptyList());

+ 추가

혹시나해서 BasicAuthenticationFilter에서 OncePerRequestFilter로 바꿔서 테스트해봤는데 StackOverflow가 동일하게 발생했다! BasicAuthenticationFilter만의 문제는 아닌 걸로... 이건 좀 더 찾아봐야겠다.

profile
기술블로그보다는 기록블로그

0개의 댓글