💬 들어가기앞서 . . .
이 문제는 Spring Security를 활용하여 JWT 인증을 구현하는 과정에서 발생했습니다.
로그를 확인하며 디버깅한 결과, Custom한 UserDetails 객체의 특정 필드가 예상한 타입이 아닌 String 타입으로 반환되면서 문제가 발생한 것을 확인할 수 있었습니다.
유사한 문제가 발생하지 않도록 발생한 오류의 원인과 해결 방법을 단계별로 정리하겠습니다.
🛠️ [트러블슈팅] Authentication Principal is not of type CustomUserDetails.
1. 문제 상황 (Issue)
[Authentication Principal is not of type CustomUserDetails. Actual type: java.lang.String]
사용자의 입력된 정보를 받아 로그인 인증 여부를 진행하며 발생한 에러입니다.
번역을 해보니, Authentication 객체의 principal 타입이 기대한 UserDetails 타입이 아닌, String 타입이기에 발생한 에러였습니다.
2. 원인 분석 (Cause)
• 문제의 원인을 찾는 과정과 분석 내용을 정리합니다.
• 로그 확인, 디버깅 과정 등을 기술하면 좋습니다.
✅ 원인 1: 인증 과정에서 반환되는 Authentication 객체의 principal 타입이 기대한 CustomUserDetail가 아닌, String type
인증 과정에서 반환되는 Authentication 객체의 principal 타입이 기대한 CustomUserDetail가 아닌, 단순한 String 즉, user_id로 되어있어서 발생하였습니다.
오류가 발생하던 기존의 코드는 아래와 같습니다.
기존에 사용자 인증을 수행하던 AuthenticationProvider 클래스의 authenticate 메소드에서는 비밀번호 검증 후, 위와 같이 단순하게 user_id (String)로 Authentication 객체를 생성했습니다.
"principal은 사용자를 유일하게 식별할 수 있는 값" 으로, user_id만으로도 사용자를 식별할 수 있으니 더 간단하지 않을까?
라는 생각으로 user_id값만 넣어줬던 것 같습니다.
이렇게 반환하면, Authentication 객체의 principal은 String 타입이 되므로, SecurityContext에서 UserDetails 기능 (getAuthorities(), getUsername())을 활용할 수 없습니다.
✅ 원인 2: LoginFilter의 successfulAuthentication 메소드에서 principal을 CustomUserDetails로 캐스팅 시도
사용자의 로그인을 처리하는 LoginFilter에서 인증을 성공했을 때 실행되는 successfulAuthentication 메서드에서 principal를 CustomUserDetails로 캐스팅해주고있었습니다.
여기서 principal이 String이므로, ClassCastException 오류가 발생하는 것입니다.
3. 해결 방법 (Solution)
• AuthenticationProvider에서 인증 성공 시, principal로 CustomUserDetails 객체를 생성하여 반환
Spring Security는 SecurityContextHolder에 저장된 Authentication 객체를 통해 인증된 사용자의 정보를 관리하기때문에 principal은 꼭! UserDetails type의 객체를 반환해주어야합니다.
기존의 코드
수정 후 코드
CustomUserDetails객체를 생성하여 user의 정보를 담아서 principal에 UserDetails 타입의 객체를 담아주었습니다.
이렇게 하면 Authentication 객체의 principal이 CustomUserDetails 타입이 되어, LoginFilter에서 안전하게 캐스팅할 수 있습니다.
4. 참고 자료 (Reference)
• 관련 문서, 공식 문서, Stack Overflow 링크 등을 추가합니다.
📌 참고 블로그:
1. 링크
2. 링크
[Spring]class java.lang.String cannot be cast to class org.springframework.security.core.userdetails.User
필자는 User타입을 쓰다가 UserDetails 타입으로 변환을하였으나 해결방법은 동일하다고 생각하여 글을 남깁니다. Spring Security + JWT를 적용하다가 어이없는 실수였지만 혹시나 필자와같은 실수를 하
csy7792.tistory.com
✨ 게시글을 마무리하며
• principal은 단순한 user_id가 아니라, 인증된 사용자의 정보를 담은 객체여야 한다는 점
• Authentication 객체를 생성할 때, CustomUserDetails를 활용해야 이후 보안 관련 기능을 정상적으로 사용할 수 있다는 점
Sequrity의 기본 이론과 principal, credentials의 개념 이해 부족에서 일어났던 에러였던 것 같습니다.
이번 문제를 해결하며, SecurityContext에서 Authentication 객체가 어떻게 관리되는지, 그리고 UserDetails를 올바르게 활용하는 방법을 배울 수 있었습니다.
이 경험을 바탕으로 앞으로는 Spring Security의 인증 흐름을 더욱 깊이 이해하고, 유사한 문제를 사전에 방지할 수 있도록 하겠습니다.
이번 트러블슈팅이 같은 문제를 겪는 분들에게 도움이 되길 바라며, 더 나은 코드 설계를 위해 지속적으로 학습해 나가겠습니다!
'Spring > Error' 카테고리의 다른 글
[ 트러블슈팅 ] 낙관적 락 동시성 처리 에러 (0) | 2025.09.19 |
---|---|
[트러블슈팅]io.jsonwebtoken.ExpiredJwtException: JWT expired 564427 (0) | 2025.03.24 |
[트러블슈팅] application.yml에서 oauth2 설정이 적용되지 않는 이슈 (0) | 2025.02.12 |
[Jwt]Error creating bean with name 'springSecurityFilterChain' defined in class path resource (0) | 2024.08.21 |
[JUit] Argument(s) are different! Wanted: 에러 (0) | 2024.07.03 |