인프런/스프링 시큐리티 완전 정복 [6.x 개정판]

10) 사용자 상세 서비스 - UserDetailsService , 사용자 상세 -UserDetails

backend dev 2024. 10. 17.

 

 

UserDetailsService

 

UserDetailsService의 주요 기능은 사용자와 관련된 상세 데이터를 로드하는 것이며

사용자의 신원, 권한, 자격 증명 등과 같은 정보를 포함할 수 있다 

 

이 인터페이를 사용하는 클래스는 주로 AuthenticationProvider 이며

사용자가 시스템에 존재하는지 여부와 사용자 데이터를 검색하고 인증 과정을 수행한다

 

사용자의 이름을 통해 사용자 데이터를 검색하고, 해당 데이터를 UserDetails 객체로 반환한다

 

 

UserDetailsService 흐름도

 

UserDetailsService 사용 방법

 

 

위의 userDetailService() 메소드를 사용한것처럼 직접 POJO객체를 등록해도 되지만

 

아래처럼 @Bean을 이용해서 빈등록을 하는것이 사용성에 유리하다. [ 빈 등록방식을 사용하자.]

 

public class CustomUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return User.withUsername("user").password("{noop}1111").roles("USER").build();    }
}

 

@EnableWebSecurity
@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated())
                .formLogin(Customizer.withDefaults())
        ;
        return http.build();
    }

    @Bean
    public UserDetailsService customUserDetailsService(){
        return new CustomUserDetailsService();
    }
    
}

 

@Component
@RequiredArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private final UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String loginId = authentication.getName();
        String password = (String)authentication.getCredentials();

        //아이디 검증
        UserDetails user = userDetailsService.loadUserByUsername(loginId);
        if(user == null) throw new UsernameNotFoundException("UsernameNotFoundException");
        //비밀번호 검증

        return new UsernamePasswordAuthenticationToken
                (user.getUsername(),  user.getPassword(), user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.isAssignableFrom(UsernamePasswordAuthenticationToken.class);
    }
}

 

사용자 상세 - UserDetails

 

 

 

UserDetails

 

사용자의 기본 정보를 저장하는 인터페이스로서 Spring Security 에서 사용 하는 사용자 타입이다

 

저장된 사용자 정보는 추후에 인증 절차에서 사용되기 위해

Authentication 객체에 포함되며 구현체로서 User 클래스가 제공된다

public class User implements UserDetails, CredentialsContainer

default boolean isCredentialsNonExpired() {
    return true;
}

• 사용자의 비밀번호가 유효 기간이 지났는지를 확인하며 유효 기간이 지난 비밀번호는 인증할 수 없다

default boolean isAccountNonExpired() {
    return true;
}

• 사용자 계정의 유효 기간이 지났는지를 나타내며 기간이 만료된 계정은 인증 할 수 없다

 

String getUsername();

• 사용자 인증에 사용된 사용자 이름을 반환하며 null 을 반환할 수 없다

Collection<? extends GrantedAuthority> getAuthorities();

• 사용자에게 부여된 권한을 반환하며 null을 반환할 수 없다

default boolean isAccountNonLocked() {
    return true;
}

• 사용자가 잠겨 있는지 아닌지를 나타내며 잠긴 사용자는 인증할 수 없다

tring getPassword();

• 사용자 인증에 사용된 비밀번호를 반환한다

default boolean isEnabled() {
    return true;
}

• 사용자가 활성화되었는지 비활성화되었는지를 나타내며 비활성화된 사용자는 인증할 수 없다

 

 

 

@Getter
@AllArgsConstructor
public class AccountDto {
    private String username;
    private String password;
    private Collection<GrantedAuthority> authorities;
}

UserInfo == AccountDto

public class CustomUserDetails implements UserDetails {

    private final AccountDto accountDto;

    public CustomUserDetails(AccountDto accountDto){

        this.accountDto = accountDto;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return accountDto.getAuthorities();
    }

    @Override
    public String getPassword() {
        return accountDto.getPassword();
    }

    @Override
    public String getUsername() {
        return accountDto.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

 

public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        AccountDto accountDto = new AccountDto("user", "{noop}1111", List.of(new SimpleGrantedAuthority("ROLE_USER")));
        return new CustomUserDetails(accountDto);
    }
}

 

@EnableWebSecurity
@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated())
                .formLogin(Customizer.withDefaults())
        ;
        return http.build();
    }

    @Bean
    public UserDetailsService customUserDetailsService(){
        return  new CustomUserDetailsService();
    }
}

 

@Component
@RequiredArgsConstructor
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private final UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        String loginId = authentication.getName();
        String password = (String)authentication.getCredentials();

        //아이디 검증
        UserDetails user = userDetailsService.loadUserByUsername(loginId);
        if(user == null) throw new UsernameNotFoundException("UsernameNotFoundException");
        //비밀번호 검증

        return new UsernamePasswordAuthenticationToken
                (user.getUsername(),  user.getPassword(), user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.isAssignableFrom(UsernamePasswordAuthenticationToken.class);
    }
}

 

댓글