Spring Security - User, UserDetails , UserDetailsService

TopOfTheHead·2025년 11월 18일

Spring Security

목록 보기
6/27

UserDetailsService :
Spring Security에서 인증 / 인가 작업을 수행 시 인증정보를 포함하는 UserDetails조회를 위해 DB에 저장된 사용자정보UserDetails 객체로서 조회하여 제공하는 Core Interface

AutheticationProvider에 의해 호출되어 DB에서 저장된 사용자정보를 조회 및 해당하는 사용자정보를 포함하는 UserDetails 구현체를 생성해서 전달하는 역할을 수행.
AutheticationProviderAuthentication 구현체CredentialsUserDetails 구현체Password를 비교하여 사용자인증검증을 수행.

  • UserDetailsService.loadUserByUsername("username") :
    DB에서 해당 자격증명ID에 해당하는 사용자 정보를 조회 후 UserDetails 객체로 반환하는 구현 메서드

    세션 로그인@Service Class에서 해당 UserDetailsService 인터페이스를 구현 후 해당 구현 메서드를 구현해야한다.
    JWT 로그인 시는 구현할 필요가 없음.
@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Optional<Member> memberByUserName = memberRepository.findMemberByUserName(username);
        Member member = memberByUserName.orElseThrow(
                () -> new UsernameNotFoundException("해당 회원을 찾을 수 없음")
        );
        return new CustomUserDetails(
                member.getUserName(),
                member.getPassword(),
                member.getRole()
        );
    }

검증 수행 시 AuthenticationProvider에게 DB에서 가져온 사용자 정보를 포함한 UserDetails DTO를 생성 후 반환
AutheticationProvider는 해당 기능을 통해 인증에 필요한 사용자 정보를 포함하는 UserDetails 구현체를 전달 받아서 검증에 활용

UserDetails :
Spring Security인증 / 인가에 필요한 사용자 별 세부 인증 정보를 저장하는 DTO 역할Core Interface.
인증 작업 수행 시 UserDetailsService에 의해 DB에 저장된 사용자이름, PW, 권한 등의 세부정보를 포함한 UserDetails 구현체 생성 후 AuthenticationProvider에게 반환

UserDetails 구현체User 클래스를 사용하거나, 커스텀 UserDetails 구현 클래스를 정의해서 생성 가능

  • UserDetails 구현클래스DTO 역할을 수행
    ex ) ~/dto 디렉토리 내 저장

    。어떤 서비스를 개발함에 따라 로그인회원이 가져야 하는 데이터는 다양하며, 커스텀 UserDetails 구현체를 통해 필요 정보만 포함하도록 설정
    ex ) 배민로그인유저주소정보가 있어야한다.

    UserDetails 구현체원본 유저 테이블에 저장된 데이터를 추출하여 Spring Security에 의해 인증이 수행되는 DTO 역할을 수행한다.
    ID / PW / Authority 외 더 필요한 정보를 추가 가능

    JWT를 통한 검증 시에도 UserDetails 구현체DTO로서 사용됨
@RestController
@RequestMapping("/api/profiles")
public class ProfileController {
    @GetMapping("/me")
    public ResponseEntity<?> getMyProfile(
@AuthenticationPrincipal CurrentUser currentUser
) {
        UUID userId = currentUser.getId();
        String email = currentUser.getEmail();
        return ResponseEntity.ok("유저 ID: " + userId + ", 이메일: " + email);
    }
}

인증 이후 인메모리 DB에 저장되며 Controller@AuthenticationPrincipal을 통해 해당 정보로 주입됨

  • Lombok@Getter를 통해 추상메서드를 축약한 커스텀 UserDetails 구현체 생성 가능
    UserDetails 인터페이스구현UserDetails 구현 클래스 생성 시 추상메서드 : getUsername(), getPassword(), getAuthorities()에 따른 usename / password / role 필드만 반드시 정의하고, 나머지 필드 정보DTO로서 선택적으로 추가해서 정의
@Getter
@RequiredArgsConstructor
public class CurrentUser implements UserDetails {
    private final String username;
    private final String password;
    private final String role;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(
                new SimpleGrantedAuthority(role)
        );
    }
}

▶ 나머지 부정검사를 수행하는 구현메서드 : isAccountNonExpired(), isAccountNonLocked(), isCredentialsNonExpired(), isEnabled() 의 경우 선택적으로 구현

  • 세션 로그인에서의 UserDetails 구현체
    세션 로그인UserDetailsService.loadUserByUsername("username")에 의해 DB에서 가져온 사용자 정보를 포함한 UserDetails DTO를 생성 후 AuthenticationProvider에게 반환함으로써 Spring Security에 의해 식별

User :
UserDetails를 구현한 구현체
UserDetails인터페이스이므로 직접 Instance를 생성할 수 없으므로 User Class의 BuilderID, PW, Role를 지정하여 UserDetails 구현체 생성.

  • User.build() :
    UserDetails Instance를 생성

  • User.builder() :
    User.withDefaultPasswordEncoder()에서 비밀번호 알고리즘( .passwordEncoder() )을 추가 정의하여 UserDetails 객체를 생성하는 Builder Method.
    User.withDefaultPasswordEncoder()를 대체.

  • User객체.passwordEncoder(input -> 패스워드인코더().encode(input)) :
    Spring Bean으로 사전등록한 특정 PasswordEncoder( ex. BCryptPasswordEncoder )를 통해 문자열인코딩하여 반환하는 함수형 인터페이스를 정의하여 해당 UserDetails객체PasswordEncoder를 설정
    ▶ 사전에 @Configuration Class에서 PasswordEncoder@Bean method가 존재해야 사용가능
	private UserDetails getUserDetails(String username, String password,roles role){
        // User.passwordEncoder(람다식)을 통해 input된 문자열을 PasswordEncoder 구현 Class를 통해
        // `Hashing`으로 Encoding을 수행하여 Hash값을 반환. 
        return User.builder().passwordEncoder((input)->
            passwordEncoderB().encode(input)
        ).username(username).password(password).roles(role.toString(),"DEVELOPER").build();
    }
profile
공부기록 블로그

0개의 댓글