Spring/Spring Security

[Spring Security] UserDetailsService

제우제우 2024. 9. 2. 21:45

로그인 ~ 세션 생성

회원가입을 하고 유저의 정보를 DB에 저장하고 스프링 시큐리티를 통해 로그인을 했을 때

어떤 방식으로 DB에서 유저 정보를 조회하고 활용할까? 

 

1. 로그인 요청

사용자가 로그인 폼에 사용자 이름과 암호를 입력하고 제출

 

2. UserDetailsService 호출

SpringSecurity UserDetailsServiceloadUserByUsername 메서드를 호출하여 사용자 이름을 기반으로 사용자의 UserDetails 객체를 조회한다. 

public interface UserDetailsService {
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
@Bean
public UserDetailsService userDetailService(UserRepository userRepository) {
     return username -> {
         User user =  userRepository.findByEmail(username)
                  .orElseThrow(() -> new UsernameNotFoundException(username + "을 찾을 수 없습니다."));
         return new UserPrincipal(user);
     };
}

 

UserDetailsService 인터페이스의 loadUserByUsername 메서드를 람다식으로 구현하였다. 

 

UserRepository (JPA Repository)

user 엔티티를 조회해서  UserPrincipal 객체로 return 하고 있다. 

 

 loadUserByUsername 메서드의 반환타입:  UserDetails 

 

UserDetails(인터페이스) → User(org.springframework.security.core.userdetails) UserPrincipal(커스텀)

 

UserPrincipal

public class UserPrincipal extends User {
    private final Long userId;
    public UserPrincipal(com.jeulog.domain.User user){
        super(user.getEmail(), user.getPassword(), List.of(new SimpleGrantedAuthority("ADMIN")));
        userId = user.getId();
    }
    public Long getUserId() {
        return userId;
    }
}

User 클래스를 상속 받은 간단한 클래스이다. 

 

3. 암호 검증

@Autowired
private PasswordEncoder passwordEncoder;

@Autowired
private AuthenticationManager authenticationManager;

public Authentication authenticate(String username, String password) {
    Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(username, password)
    );
    return authentication;
}

 

UserDetailsService가 반환한 UserDetails 객체의 암호를 PasswordEncoder를 사용하여 제출된 암호와 비교

 

4. 세션 관리

인증이 성공하면, Spring Security는 새로운 HTTP 세션을 생성

이 세션에는 SecurityContext 객체가 포함되며, 이 객체에는 Authentication 객체가 저장

 

UserDetailsService 역할 및 정리 

UserDetailsService의 주된 역할은 데이터베이스에서 사용자 정보를 조회하고, 해당 정보를 UserDetails 객체로 변환하여 Spring Security의 인증 과정에 제공하는 것

@Bean
public UserDetailsService userDetailService(UserRepository userRepository) {
     return username -> {
         User user =  userRepository.findByEmail(username)
                  .orElseThrow(() -> new UsernameNotFoundException(username + "을 찾을 수 없습니다."));
         return new UserPrincipal(user);
     };
}

 

사용자 정보 조회

loadUserByUsername(String username) 메서드를 구현하여 데이터베이스에서 사용자 정보를 조회

 

데이터 유효성 검사

데이터베이스에서 사용자 정보를 조회한 후, 해당 사용자가 존재하지 않는 경우 UsernameNotFoundException을 발생

 

UserDetails 객체 생성

조회된 사용자 정보를 기반으로 UserDetails 객체를 생성하여 반환

UserDetails 객체는 사용자 이름, 암호(암호화된 상태), 권한(roles) 등 인증에 필요한 정보를 포함

이 객체는 Spring Security의 인증 및 권한 부여 과정에서 사용

 

인가(권한 부여) 과정으로 넘기기

UserDetailsService에서 반환된 UserDetails 객체는 Spring Security의 인증 매커니즘에 전달되어 사용자의 인증과 권한 검사가 진행

인증이 성공하면, UserDetails 객체는 Authentication 객체로 래핑되어 SecurityContext에 저장되며, 이후 요청에서 사용자 정보를 사용할 수 있다.