[React + SpringBoot] JWT 인증 구현 ③ - UserDetails, UserDetailsService

SihoonCho·2023년 4월 19일
0
post-thumbnail

※ 읽기에 앞서


본 시리즈는 작성자의 이해와 경험을 바탕으로 실습 위주의 설명을 기반으로 작성되었습니다.
실습 위주의 이해를 목표로 하기 때문에 다소 과장이 많고 생략된 부분이 많을 수 있습니다.
따라서, 이론적으로 미흡한 부분이 있을 수 있는 점에 대해 유의하시기 바랍니다.

또한, 본 시리즈는 ChatGPT의 도움을 받아 작성되었습니다.
수 차례의 질문을 통해 도출된 여러가지 다양한 방식의 코드를 종합하여
작성자의 이해와 경험을 바탕으로 가장 정석으로 생각되는 코드를 재정립하였습니다.


📌 UserDetails


Spring Security 에서 인증에 사용하기 위한 User 정보를 담은 객체.
public interface UserDetailsimplements하여 CustomUserDetails로 커스텀.

SpringBoot WebSecurity에서 JWT(JSON Web Token)의 데이터 흐름은 다음과 같습니다.

CustomUserDetails -> CustomUserDetailsService ->
JwtTokenProvider -> JwtTokenFilter -> WebSecurityConfig


📖 CustomUserDetails.java


CustomUserDetails.java

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Collections;

public class CustomUserDetails implements UserDetails {
	private final User user;
    
    // Constructor
    public CustomUserDetails(User user) {
    	this.user = user;
    }
    
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Collections.singleton(new SimpleGrantedAuthority(user.getRole().name()));
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public Long getId() {
    	return user.getId();
    }
    
    public String getEmail() {
    	return user.getEmail();
    }
    
    public String getContact() {
    	return user.getContact();
    }
    
    @Override
    public String getUsername() {
        return user.getUsername();
    }
    
    @Override
    public String getPassword() {
        return user.getPassword();
    }
    
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    @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 interface UserDetailsUser.java를 기반으로 implements한다.
쉽게 말하면 User.javaAuthority가 추가된 CustomUser Class인 느낌이다.


📌 UserDetailsService


Spring SecurityUserDetails를 사용하기 위한 Service.
public interface UserDetailsServiceimplements하여
CustomUserDetailsService로 커스텀.

SpringBoot WebSecurity에서 JWT(JSON Web Token)의 데이터 흐름은 다음과 같습니다.

CustomUserDetails -> CustomUserDetailsService ->
JwtTokenProvider -> JwtTokenFilter -> WebSecurityConfig


📖 CustomUserDetailsService.java


import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
    private final UserRepository userRepository;	// 별도로 생성해야 함

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username).orElseThrow(
                () -> new UsernameNotFoundException("해당 유저가 존재하지 않습니다. username = " + username));
        return new CustomUserDetails(user);	// 위에서 생성한 CustomUserDetails Class
    }
    
    // 필요시 추가
    public UserDetails loadUserByUserId(Long userId) throws IllegalArgumentException {
        User user = userRepository.findById(userId).orElseThrow(
                () -> new IllegalArgumentException("해당 유저가 존재하지 않습니다. user_id = " + userId));
        return new CustomUserDetails(user);
    }
    
    // 필요시 추가
    public UserDetails loadUserByEmail(String email) throws IllegalArgumentException {
        User user = userRepository.findByEmail(email).orElseThrow(
                () -> new IllegalArgumentException("해당 유저가 존재하지 않습니다. email = " + email));
        return new CustomUserDetails(user);
    }
}

public interface UserDetailsServiceUser.java를 기반으로 implements한다.
UserRepository를 통해 User Entity에 접근하고 Authority가 추가된
CustomUserDetails 객체를 반환하는 과정을 거친다.

필요에 따라 user_id, user_email 등을 기반으로 User Entity를 찾는 메소드
loadUserByUserId(), loadUserByEmail() 등을 구현해서 사용한다.

예를 들어, 프로젝트 요구사항에 따라
Github처럼 username이 중복을 허용하는 경우
id, email 등을 사용하여 User 정보에 접근합니다.

profile
꾸준히 노력하는 개발자

0개의 댓글