이 문서는 CustomUserDetails 클래스의 역할과 JWT 기반의 stateless 인증 시스템에서의 사용 시점을 명확히 설명합니다.
CustomUserDetails은 Spring Security의 UserDetails 인터페이스를 구현한 클래스입니다.
Member 엔티티 전체가 아닌, 인증에 필수적인 최소한의 데이터만 포함하여 보안성과 효율성을 높입니다.// CustomUserDetails의 주요 필드
private final Long id;
private final String email;
private final Collection<? extends GrantedAuthority> authorities;
CustomUserDetails의 사용 시점을 이해하기 위해 두 과정을 구분해야 합니다.
CustomUserDetails 사용 여부: 사용하지 않습니다.AuthService에서 전달받은 비밀번호와 DB에 저장된 해시된 비밀번호를 비교하여 사용자를 검증합니다. 검증 성공 시, JWT(Access Token, Refresh Token)를 생성하여 클라이언트에게 반환합니다.CustomUserDetails 사용 여부: 매우 중요하게 사용됩니다.SecurityConfig에 등록된 커스텀 JWT 필터가 요청을 가로챕니다.CustomUserDetails 생성: 토큰이 유효하면, 토큰의 claims에 담긴 사용자 정보(ID, 이메일, 권한 등)를 추출하여 메모리 상에 CustomUserDetails 객체를 생성합니다.CustomUserDetails을 Authentication 객체로 감싸 SecurityContextHolder에 등록합니다. 이로써 Spring Security는 현재 요청의 주체를 인식하게 됩니다.@AuthenticationPrincipal 어노테이션을 통해 컨트롤러에서 현재 사용자 정보를 직접 주입받거나, @PreAuthorize 등으로 권한 검사를 수행할 수 있습니다.Stateless 원칙은 서버에 사용자 세션을 저장하지 않는다는 의미이지, 요청을 처리하는 메모리 상의 일시적인 정보까지 사용하지 않는다는 의미가 아닙니다.
CustomUserDetails은 서버에 저장되는 "상태(State)"가 아니라, 매 요청마다 JWT를 기반으로 생성되는 일회성 "신분증"입니다. 이 신분증이 있어야 Spring Security의 강력한 보안 기능들을 원활하게 활용할 수 있습니다.
sequenceDiagram
participant Client
participant "JWT Filter" as Filter
participant "AuthService/DB" as Service
participant Controller
participant "Spring Security" as Security
Note over Client, Service: 1. 최초 로그인 (Authentication) - CustomUserDetails 사용 안함
Client->>Service: 로그인 요청 (이메일, 비밀번호)
activate Service
Service->>Service: DB 정보로 사용자 검증
Service-->>Client: JWT 발급 (Access/Refresh Token)
deactivate Service
Note over Client, Security: 2. API 요청 (Authorization) - CustomUserDetails 사용
Client->>Filter: API 요청 (with JWT)
activate Filter
Filter->>Filter: 1. JWT 유효성 검증
Note right of Filter: 2. 토큰 정보로 CustomUserDetails 생성
Filter->>Security: 3. SecurityContext에 Authentication 등록
Filter->>Controller: 4. 요청 전달
deactivate Filter
activate Controller
Controller->>Security: @AuthenticationPrincipal 요청
activate Security
Security-->>Controller: SecurityContext에서 CustomUserDetails 제공
deactivate Security
Controller-->>Client: API 응답
deactivate Controller
package eightbit.moyeohaeng.global.security;
import java.util.Collection;
import java.util.Collections;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import eightbit.moyeohaeng.domain.member.entity.member.Member;
import lombok.Getter;
@Getter
public class CustomUserDetails implements UserDetails {
private final Long id;
private final String email;
private final Collection<? extends GrantedAuthority> authorities;
private CustomUserDetails(Long id, String email, Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.email = email;
this.authorities = authorities;
}
public static CustomUserDetails from(Member member) {
return new CustomUserDetails(
member.getId(),
member.getEmail(),
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))
);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return email;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
| 시점 | CustomUserDetails 사용 여부 | 설명 |
|---|---|---|
| 최초 로그인 | ❌ | 이메일/비밀번호로 DB와 직접 비교하여 인증. |
| 로그인 후 API 요청 | ✅ | JWT 검증 후, 토큰 정보로 CustomUserDetails을 생성하여 SecurityContext에 등록. 이후 권한 검사 및 사용자 정보 참조에 사용. |