@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
는 나중에 작성하겠다.
@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")
으로 사용해도 무방하다.
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
DB로부터 email 값을 이용해 User 객체를 불러오기 위한 메소드이다.
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로 반환하게만 해두었다.
@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 객체를 리턴해 인증 과정에 사용하게 된다.