본문 바로가기
프로그래밍/기타

[Spring Security] Spring Security에서 UserDetailsService와 PasswordEncoder Interface 분석하기

by 사바라다 2022. 12. 18.

안녕하세요. 이전시간에 Spring Security의 전체적인 아키텍쳐와 기본적인 샘플에 대해서 알아보았습니다. 아래가 Spring Security의 전체적인 이미지였습니다. 오늘은 이어서 UserDetailsService와 PasswordEncoder의 디테일한 부분에 대해서 알아보도록 하겠습니다.

UserDetailsService와 UserDetailsManager interface

UserDetailsService는 이전시간에 사용자에 대한 세부 정보를 가져오는 역할을 한다고 했습니다. 실제로 인터페이스를 확인해서 UserDetailsService는 어떻게 활용해야하는지 확인해보도록 합시다.

/**
 * 유저의 데이터를 가져오는데 사용하는 Core Interface
 *
 * 이 인터페이스는 read-only method만을 가지며 data-access에 대해서 strategies 패턴을 사용합니다.
 */
public interface UserDetailsService {

    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

해당 Interface에서는 실제로 loadUserByUsername라고하는 method 하나만 존재하고 있습니다. 이는 read-only method로 username을 이용하여 UserDetails를 조회하는 메서드입니다. 즉, 해당 Interface는 위에서 언급한대로 유저의 정보를 가져오기만 합니다.

유저의 정보를 저장하고 수정하는 Interface는 별도로 존재합니다. 바로 UserDetailsManager라는 Interface입니다. 이 인터페이스의 구조는 아래와 같습니다.

/**
 * UserDetailsService의 확장된 Interface로 유저의 create와 update가 가능합니다.
 */
public interface UserDetailsManager extends UserDetailsService {

    /**
     * 새로운 유저를 생성합니다.
     */
    void createUser(UserDetails user);

    /**
     * 유저를 수정합니다.
     */
    void updateUser(UserDetails user);

    /**
     * 유저를 삭제합니다.
     */
    void deleteUser(String username);

    /**
   * 유저의 패스워드를 변경합니다.
     */
    void changePassword(String oldPassword, String newPassword);

    /**
     * username으로 유저가 존재하는지 검사합니다.
     */
    boolean userExists(String username);
}

UserDetailsManager 인터페이스는 UserDetailsService 인터페이스의 확장 버전 답게 좀 더 User에 대해서 다양한 역할을 수행할 수 있는것을 interface를 통해 알 수 있습니다. 이러한 인터페이스의 분할(Interface Segregation)로 인해서 우리는 경우에 따라 적절한 인터페이스를 선택해서 수행할 수 있습니다.

추가로 여기에 지속적으로 등장하는 User에 대한 정보 Interface Model이 있습니다. 바로 user Details 인터페이스입니다. 해당 인터페이스는 Spring Security에서 정의한 User에 대한 정의입니다. 해당 정의는 아래와 같습니다.

/**
 * User 정보를 제공
 *
 * 
 * 예시로써 Spring Securtiy에서 구현해둔 User라는 구현 클래스를 제공합니다.
 */
public interface UserDetails extends Serializable {

    /**
     * 유저가 가지고 있는 권한을 반환합니다.
     */
    Collection<? extends GrantedAuthority> getAuthorities();

    /**
     * 유저의 패스워드를 반환합니다.
     */
    String getPassword();

    /**
     * username을 반환합니다.
     */
    String getUsername();

    /**
     * 유저의 계정이 만료되었는지 여부를 나타냅니다.
     */
    boolean isAccountNonExpired();

    /**
     * 유저의 계정이 잠겼는지 여부를 나타냅니다.
     */
    boolean isAccountNonLocked();

    /**
   * 유저의 Credential이 만료되었는지 여부를 나타냅니다.
     */
    boolean isCredentialsNonExpired();

    /**
     * 유저를 사용할 수 있는지 여부를 나타냅니다.
     */
    boolean isEnabled();

}

이렇게해서 Spring Security에서 UserDetailsService 관련있는 interface들을 모두 살펴보았습니다. 정리하면 아래와 같습니다.

  • UserDetailsService : User 정보를 읽어오는 interface
  • UserDetailsManager : User 정보 쓰고 수정하는 interface
  • UserDetails : User 정보의 구현 model interface

PasswordEncoder Interface

2번째로 알아볼 컴포넌트는 PasswordEncoder 입니다. PasswordEncoder는 암호를 인코딩하고 암호가 정확한지 판단하는 method를 가진 interface 입니다. 이를 이용하여 userDetailsService로 가져온 User의 패스워드가 정확한지 판단하는 등의 일을 하게 됩니다. interface는 아래와 같습니다.

/**
 * 패스워드 인코딩 관련 Service interface
 */
public interface PasswordEncoder {

    /**
   * raw password를 인코딩한다.
     */
    String encode(CharSequence rawPassword);

    /**
     * 들어온 password와 storage에 가지고 있던 encodedPassword를 비교하여 맞는지 여부를 반환합니다. true이면 match, false면 match하지 않습니다.
     */
    boolean matches(CharSequence rawPassword, String encodedPassword);

    /**
   * encoding 업그레이드가 가능하다면 true 아니라면 false를 반환합니다.
     */
    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}

마무리

오늘은 이렇게 Spring Security의 UserDetailsService와 PasswordEncoder의 Interface에 대해서 알아보는 시간을 가져보았습니다. 다음 시간에는 오늘 배운 Interface를 어떻게 Spring Security에서 커스터마이징하고 사용할 수 있는지 JPA와 함께 실습으로 살펴보는 시간을 가져보도록 하겠습니다.

감사합니다.

참조

[1] Spring Security In Action

[2] spring_io_docs_PasswordEncoder

댓글