JPA Column Enum으로 관리하기
회원 정보인 Member Entity에서 권한을 관리하는 state라는 필드가 있는데 기존에는 state를 String으로 사용했었다. 그런데 String으로 사용하면 생길 수 있는 문제점이 있는데, 현재 진행중인 프로젝트의 권한은 USER, ADMIN, ANONYMOUS(승인되지 않은 유저) 이렇게 세 개로 관리하는데 권한 변경하는 API에서 Request에 권한을 받을 때 잘 못된 값이 들어 올 수 있다는 위험이 존재한다.
이런 문제를 사전에 차단하기 위해 권한에 대한 Enum을 만들어 잘못된 입력 값이 들어오면 막아주도록 설계를 변경했다.
ROLE Enum
public enum Role {
ROLE_USER("ROLE_USER"),
ROLE_ANONYMOUS("ROLE_ANONYMOUS"),
ROLE_ADMIN("ROLE_ADMIN");
String role;
Role(String role) {
this.role = role;
}
public String value() {
return role;
}
}
Member Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "email")
private String email;
@Column(name = "department")
private String department;
@Column(name = "state")
@Enumerated(EnumType.STRING)
private Role state;
/*...*/
}
Entity에 있는 Enum이 데이터베이스에 그대로 저장되려면 @Enumerated(EnumType.STRING) 어노테이션을 사용하면 DB에 Enum 값이 그대로 String으로 저장된다.
CustomUserDetails
Spring Security를 사용하는 경우
CustomUserDetails의 createUserDetails에서 권한을 넣어주는 부분에서 Enum을 toString 메소드로 String으로 변환한 뒤에 SimpleGrantedAuthority로 권한을 지정해준다.
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final MemberRepository memberRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return memberRepository.findByLoginId(username).map(this::createUserDetails)
.orElseThrow(() -> new UsernameNotFoundException(username + "는 데이터베이스에 없는 데이터입니다."));
}
private UserDetails createUserDetails(Member member) {
String role = member.getState().value();
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role);
return new org.springframework.security.core.userdetails.User(
String.valueOf(member.getId()),
member.getPassword(),
Collections.singleton(grantedAuthority)
);
}
}
SecurityConfig
Security에서 admin에 대해 ADMIN 권한이 있어야만 접근이 가능하도록 설정할 수 있다.
권한을 지정할 때는 “ROLE_ADMIN”이었지만 Spring Security에서 prefix를 자동으로 "ROLE_"을 넣어주므로 이 때 hasRole에는 ROLE을 제외하고 뒷 부분인 ADMIN만 써주면 된다.
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**/*").permitAll()
.antMatchers("/image/**").permitAll()
.antMatchers("/auth/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers(HttpMethod.GET, "/product/**").permitAll() // 상품쪽은 조회만 권한없이 가능
번외. No enum constant 오류
이렇게 enum으로 변환하다가 가끔 No enum constant enum.값 이라는 에러가 발생할 때가 있다.
이 에러가 발생하는 원인은 Entity에서 사용하는 Enum에 존재하지 않는 값이 데이터베이스의 해당 컬럼에 존재할 경우 발생하는 오류이다. (대소문자를 구분한다.)
이 에러를 해결하려면 Enum에 존재하지 않는 값을 DB에서 삭제하거나, 반대로 DB에 존재하는 값을 Enum에 추가시켜주면 해결된다.
'JAVA > Spring' 카테고리의 다른 글
Stomp WebSocket + JWT + Spring Security 채팅 구현 (3) | 2022.05.21 |
---|---|
@Qualifier와 @Primary 어노테이션 사용법 (0) | 2022.05.13 |
[Spring] DTO 클래스 깔끔하게 관리하기 (0) | 2022.05.07 |
[Spring] Spring Data Redis로 JWT RefreshToken 관리하기 (0) | 2022.04.12 |
[Spring] JPA를 사용한 카테고리 (하위메뉴) 구현 (2) | 2022.04.10 |