[JWT] JWT 구현하기(Feat. Redis) (2) - User, Role, UserRepository, UserDetailsImpl, UserdetailsServiceImpl,

u-nij·2022년 10월 24일
0

JWT 구현하기

목록 보기
3/8
post-thumbnail

User.class

@Entity(name = "users")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long id;

    private String email; // Principal

    private String password; // Credential
    
    @Enumerated(EnumType.STRING)
    private Role role; // 사용자 권한
    
    // == 생성 메서드 == //
    public static User registerUser(AuthDto.SignupDto signupDto) {
        User user = new User();

        user.email = signupDto.getEmail();
        user.password = signupDto.getPassword();
        user.role = Role.USER;

        return user;
    }
}

사용자 정보를 저장하거나 비즈니스 로직을 처리하기 위해서는 추가적인 필드, 메서드들을 구현해야 한다. 인증의 용도로 사용하는 UserDetails와 분리할 필요성을 느껴 별도로 클래스를 생성했다. AuthDto는 나중에 작성하겠다.

Role.class

@Getter
@RequiredArgsConstructor
public enum Role {
    ADMIN("ROLE_ADMIN", "관리자"),
    USER("ROLE_USER", "일반 사용자");

    private final String key;
    private final String title;
}

DB에 ROLE_이라는 접두어를 붙여 저장하고 싶지 않을 때 이처럼 사용할 수 있다. hasRole("ADMIN")이라고 작성시 UserDetailsService에서 Authorities를 가져와 확인할 때 자동으로 ROLE_이라는 접두어를 붙여 확인한다.
SecurityConfig에서 리소스의 권한을 설정할 때 hasRole("ADMIN") 대신에 hasAuthority("ROLE_ADMIN")으로 사용해도 무방하다.

UserRepository.interface

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

DB로부터 email 값을 이용해 User 객체를 불러오기 위한 메소드이다.

UserDetailsImpl.class

public class UserDetailsImpl implements UserDetails {

    private final User user;

    public UserDetailsImpl(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(() -> user.getRole().getKey()); // key: ROLE_권한
        return authorities;
    }

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

    @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;
    }
}

유저의 정보를 가져오는 UserDetails 인터페이스를 상속하는 클래스이다. Authentication을 담고 있다. user.getRole().getKey()를 통해 사용자의 권한(Authorities)를 부여해 가져올 수 있다. Principal과 Credential로 사용할 필드를 각각 User.email, User.password로 정해두었다. 세부 설정은 현재로써 필요하지 않기 때문에 true로 반환하게만 해두었다.

UserDetailsServiceImpl.class

@Component
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetailsImpl loadUserByUsername(String email) throws UsernameNotFoundException {
        User findUser = userRepository.findByEmail(email)
                .orElseThrow(() -> new UsernameNotFoundException("Can't find user with this email. -> " + email));

        if(findUser != null){
            UserDetailsImpl userDetails = new UserDetailsImpl(findUser);
            return  userDetails;
        }

        return null;
    }
}

DB에서 사용자의 정보를 직접 가져오는 인터페이스이다. loadUserByUsername(String username)을 오버라이드해 구현했다. 이 메소드를 사용해 UserDetails를 구현한 UserDetailsImpl 객체를 리턴해 인증 과정에 사용하게 된다.

profile
삶은 달걀이다

0개의 댓글