Spring/Error

[트러블 슈팅] Authentication Principal is not of type CustomUserDetails. Actual type: java.long.String

haenni 2025. 3. 1. 22:00

💬 들어가기앞서 . . .

이 문제는 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의 인증 흐름을 더욱 깊이 이해하고, 유사한 문제를 사전에 방지할 수 있도록 하겠습니다.

이번 트러블슈팅이 같은 문제를 겪는 분들에게 도움이 되길 바라며, 더 나은 코드 설계를 위해 지속적으로 학습해 나가겠습니다!