본문 바로가기
개발 이론/Spring Security

[Spring Security] UserDetailService, UserDetails

by dal_been 2023. 11. 1.
728x90

UserDetailsService : 사용자이름으로 사용자를 검색

UserDetailsManager : 대부분의 애플리케이션에 필요한 사용자 추가, 수정, 삭제 작업

 

UserDetailService는 UserDetails 계약을 이용해 사용자를 기술

UserDetails는 GrantedAuthority 인터페이스를 이용해 권한을 하나이상 가지게 함

 

> UserDetails

 

UserDetails 계약은 스프링 시큐리티가 이해하는 방식으로 사용자를 나타낸 것이다

 

public interface UserDetails extends Serializable{
	//사용자 자격증명 반환 메서드
	String getUserName();
    String getPassword();
    
    //앱 사용자가 수행할 수 있는 작업을 인스턴스의 컬렉션으로 반환
    Collection <? extends GrantedAuthority> getAuthorities();
    
    //사용자 계정을 필요에따라 활성화 또는 비활성화하는 네 메서드
    boolean isAccountNonExpired();
    boolean isAccountNonLocked();
    boolean isCredentialsNonExpired();
    boolean isEnabled();
}

 

GrantedAuthority

- 사용자에게 허가된 작업 = 권한

- 일반적으로 하나 이상의 권한을 가진다

- SimpleGrantedAuthority

  •  GrantedAuthority 인터페이스 구현체중 하나
  • 권한을 저장하기 위해 저장하고자하는 권한을 문자열 값으로 SimpleGrantedAuthority생성자 파라미터에 넣어줌

-> 권한 객체 생성 끝

 

 


> UserDetailsService

public interface UserDetailsService{

	UserDetails loadUserByUserName(String username) throws UsernameNotFoundException;
    
}

 

스프링 시큐리티에서 유저 정보를 가져오는 인터페이스

여기서 username은 고유하다고 간주되며 username을 가진 사용자의 세부정보를 얻는다

 

 

AuthenticationProvider는 인증논리를 구현하고 UserDetailsService를 이용해 사용자 세부 정보를 로드하는 구성 요소이며

사용자 이름으로 사용자 찾기를 위해 loadUserByUserName(String username) 메서드 호출

 

 


> UserDetailsManager

 

UserDetailsManager은 유저 생성, 수정, 삭제하는 역할을 맡는다.

물론 UserDetailsService를 확장한 거라서 loadUserByUserName도 사용가능하다

 

public interface UserDetailsManager extends UserDetailsService{
	
    void createUser(UserDetails user);
    void updateUser(UserDetails user);
    void deleteUser(String username);
    void changePassword(String oldPassword,String newPassword);
    boolean userExists(String username);
}

 

UserDetailsManager를 구현한 구현체들이 다양하게 있다

 

InMemoryUserDetailsManager

- 주로 개발 및 테스트 목적으로 실제 데이터베이스 연결 및 사용자 관리 시나리오 설정하지 않고 빠르게 사용자 인증 및 권한 관리를 구현

- SpringBoot 내부 메모리에 저장하기 때문에 애플리케이션 프로세스 종료되면 정보들이 날아감

- 하드 코딩된 사용자 정보를 관리가능

@Bean
public UserDetailsService userDetailsService() {

    User.UserBuilder users = User.withDefaultPasswordEncoder();
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(users.username("user").password("password").roles("USER").build());
    manager.createUser(users.username("admin").password("password").roles("USER", "ADMIN").build());
    return manager;
    
}

 

 

JdbcUserDetailsManager

- JDBC를 통해 데이터 베이스에 저장된 사용자 정보를 기반으로 사용자 관리를 제공하는 UserDetailsManager 구현체

- 데이터 베이스를 이용하기에 유저 정보관련 테이블과 username, password,enabled 이 세칼럼이 반드시 있어야함

 

 

JdbcUserDetailsManager 에 대해서 더 알아보자

 

기본적으로 users와 authorities 두개의 테이블이 필요하다

users에는 사용자이름, 암호, 사용자활성화 여부를 저장할 열이 꼭 필요하다

 

 

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public UserDetailsService userDetailsService(DataSource dataSource) {
      
        return new JdbcUserDetailsManager(dataSource);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

}

 

앞의 코드에서는 users, authoriteis라는 테이블을 JdbcUserDetailsManager가 예상하기 때문에 해당 테이블을 가지고 실행하지만 만약 다르게 작성한 테이블 이름으로 하고 싶다면

 

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

  @Bean
  public UserDetailsService userDetailsService(DataSource dataSource) {
    String usersByUsernameQuery = "select username, password, enabled from spring.users where username = ?";
    String authsByUserQuery = "select username, authority from spring.authorities where username = ?";
    var userDetailsManager = new JdbcUserDetailsManager(dataSource);
    userDetailsManager.setUsersByUsernameQuery(usersByUsernameQuery);
    userDetailsManager.setAuthoritiesByUsernameQuery(authsByUserQuery);
    return userDetailsManager;

  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return NoOpPasswordEncoder.getInstance();
  }

}

이렇게 JdbcUserDetailsManager의 쿼리를 재정의하면 된다

 

 

LdapUserDetailsManger

- LDAP디렉토리에서 사용자 정보를 관리하는데 사용

- LDAP란 네트워크 사에서 조지깅나, 조직 내 파일/개인정보/디바이스 정보등을 찾아볼 수 있도록 하는 프로토콜

-> 트리 구조 내에서 해당 항목의 위치를 고유하게 식별함

 

 

코드를 실행하기 위해서 LDIF파일을 생성하여 resources 폴더에 추가해야한다

또한 관련 dependency와 application properties파일도 추가해야한다

@Configuration
public class ProjectConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        var cs = new DefaultSpringSecurityContextSource("ldap://127.0.0.1:33389/dc=springframework,dc=org");
        cs.afterPropertiesSet();

        LdapUserDetailsManager manager = new LdapUserDetailsManager(cs);
        manager.setUsernameMapper(
                new DefaultLdapUsernameToDnMapper("ou=groups", "uid"));
        manager.setGroupSearchBase("ou=groups");
        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

}

 

이밖에도 내가 따로 커스텀하여 UserDetailsManager 구현할 수 있다.

 

 

 


정리

  • UserDetails 인터페이스는 스프링 시큐리티에서 사용자를 기술하는데 이용되는 계약이다
  • UserDetailsService인터페이스는 애플리케이션이 사용자 세부 정보를 얻는 방법을 설명하기 위해 스프링 시큐리티의 인증 아키텍쳐에서 구현해야하는 계약이다
  • UserDetailsManager 인터페이스는 UserDetailsService를 확장하고 사용자 생성, 변경, 삭제와 관련된 동작을 추가한다
  • 스프링 시큐리티는 UserDetailsManager 계약의 여러 구현을 제공한다. InMemoryUserDetailsManger, JdbcUserDetailsManager, LdapUserDetailManager가 있다
  • JdbcUserDetailsManger는 JDBC를 직접이용하므로 애플리케이션이 다른 프레임워크에 고정되지 않는 다는 이점이 있다