1. Role 엔티티 추가
새로운 Role 엔티티를 생성하여 권한 정보를 관리합니다.
package net.macaronics.springboot.webapp.entity; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @Entity @Getter @Setter @NoArgsConstructor @Table(name = "tbl_roles") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "role_id") private Long id; @Enumerated(EnumType.STRING) @Column(nullable = false, unique = true) private RoleType name; public Role(RoleType name) { this.name = name; } }
그리고 RoleType enum은 다음과 같이 선언합니다:
package net.macaronics.springboot.webapp.enums; public enum RoleType { USER, ADMIN, MANAGER, SUPPORT }
2. User 엔티티 수정 - 다대다 관계 설정
여러 권한을 가질 수 있도록 User 엔티티를 수정하여 Role과 다대다 관계를 설정합니다.
package net.macaronics.springboot.webapp.entity; import java.time.LocalDate; import java.util.HashSet; import java.util.Set; import jakarta.persistence.*; import lombok.*; @Entity @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder @Table(name = "tbl_users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") private Long id; @Column(nullable = false, unique = true) private String username; private String password; private String name; @Column(unique = true) private String email; private LocalDate birthDate; // 다대다 관계 설정 @ManyToMany(fetch = FetchType.EAGER) @JoinTable( name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id") ) private Set<Role> roles = new HashSet<>(); // 권한 추가 메소드 public void addRole(Role role) { this.roles.add(role); } }
@ManyToMany를 사용해 User와 Role이 다대다 관계임을 설정하고, 연결 테이블 user_roles를 통해 관계를 맺습니다.
3. 권한 설정 및 검증
다양한 권한을 부여하고 검증하기 위해 다음을 수행할 수 있습니다.
- 권한 부여: 사용자 생성 시 필요한 권한을 User 객체에 추가합니다.
- 권한 검증: Spring Security의 권한 체크 기능을 통해 API별로 필요한 권한을 설정하고 검증합니다.
4. Spring Security 설정
각 권한별로 접근을 제한하려면 Spring Security 설정을 통해 권한 기반 접근 제어를 적용할 수 있습니다.
import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/manager/**").hasRole("MANAGER") .antMatchers("/support/**").hasRole("SUPPORT") .antMatchers("/user/**").hasAnyRole("USER", "ADMIN", "MANAGER", "SUPPORT") .anyRequest().authenticated() .and() .formLogin() .and() .logout(); } }
여기서 hasRole() 메소드를 사용해 특정 URL에 대해 해당 역할을 가진 사용자만 접근하도록 설정할 수 있습니다.
5. 예제 - 사용자 권한 추가
예를 들어, 새로운 사용자를 만들면서 ADMIN과 MANAGER 권한을 부여하려면 아래와 같은 방식으로 사용 가능합니다.
// 예시: 새로운 사용자를 생성하고 권한 부여 User user = new User(); user.setUsername("testUser"); user.setPassword("password"); // 권한 추가 Role adminRole = roleRepository.findByName(RoleType.ADMIN).orElseThrow(); Role managerRole = roleRepository.findByName(RoleType.MANAGER).orElseThrow(); user.addRole(adminRole); user.addRole(managerRole); userRepository.save(user);
RoleRepository
import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import net.macaronics.springboot.webapp.entity.Role; import net.macaronics.springboot.webapp.enums.RoleType; public interface RoleRepository extends JpaRepository<Role, Long>{ Optional<Role> findByName(RoleType roleType); }
UserService
import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import net.macaronics.springboot.webapp.dto.user.UserLoginFormDTO; import net.macaronics.springboot.webapp.dto.user.UserResponse; import net.macaronics.springboot.webapp.dto.user.UserUpdateFormDTO; import net.macaronics.springboot.webapp.entity.Role; import net.macaronics.springboot.webapp.entity.User; import net.macaronics.springboot.webapp.enums.RoleType; import net.macaronics.springboot.webapp.exception.ResourceNotFoundException; import net.macaronics.springboot.webapp.repository.RoleRepository; @Service @RequiredArgsConstructor @Transactional public class UserService { private final RoleRepository roleRepository; //권한 추가 public void addSupportRoleToUser(User user, RoleType roleType) { // RoleType.SUPPORT 권한을 RoleRepository에서 가져오기 Role supportRole = roleRepository.findByName(roleType) .orElseThrow(() -> new IllegalArgumentException("RoleType SUPPORT not found")); // User 객체에 SUPPORT 권한 추가 user.addRole(supportRole); } }
PrincipalDetails
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import lombok.Getter; import net.macaronics.springboot.webapp.entity.Role; import net.macaronics.springboot.webapp.entity.User; @Getter public class PrincipalDetails implements UserDetails { @Serial private static final long serialVersionUID = 1L; private final User user; private final Long id; private final String username; private final Set<Role> roles; public PrincipalDetails(User user) { this.user = user; this.id = user.getId(); this.username = user.getUsername(); this.roles = user.getRoles(); // Set<Role>을 가져옴 } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return roles.stream() .map(role -> new SimpleGrantedAuthority(role.getName().name())) // RoleType을 가져와서 GrantedAuthority로 변환 .collect(Collectors.toList()); } public List<String> getRoleNames() { return roles.stream() .map(role -> role.getName().name()) // Role 엔티티에서 이름을 가져오는 방법에 따라 다름 .collect(Collectors.toList()); } /** * 사용자를 인증하는 데 사용된 암호를 반환합니다. */ @Override public String getPassword() { return user.getPassword(); } /** * 사용자를 인증하는 데 사용된 사용자 이름을 반환합니다. null을 반환할 수 없습니다. */ @Override public String getUsername() { return user.getUsername(); } /** * 사용자의 계정이 만료되었는지 여부를 나타냅니다. 만료된 계정은 인증할 수 없습니다. */ @Override public boolean isAccountNonExpired() { return true; } /** * 사용자가 잠겨 있는지 또는 잠금 해제되어 있는지 나타냅니다. 잠긴 사용자는 인증할 수 없습니다. */ @Override public boolean isAccountNonLocked() { return true; } /** * 사용자의 자격 증명(암호)이 만료되었는지 여부를 나타냅니다. 만료된 자격 증명은 인증을 방지합니다. */ @Override public boolean isCredentialsNonExpired() { return true; } /** * 사용자가 활성화되었는지 비활성화되었는지 여부를 나타냅니다. 비활성화된 사용자는 인증할 수 없습니다. */ @Override public boolean isEnabled() { // 우리 사이트 1년동안 회원이 로그인을 안하면!! 휴먼 계정으로 하기로 함. // 현재시간-로긴시간=>1년을 초과하면 return false; return true; } }
이렇게 구성하면 사용자는 여러 권한을 가질 수 있으며, Spring Security를 통해 각 권한별 접근 제어를 세밀하게 설정할 수 있습니다.
더미 데이터
-- tbl_users 데이터 삽입 INSERT INTO tbl_users (username, password, name, email, birth_date) VALUES ('user1', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저1', 'user1@gmail.com', '1994-09-29'), ('user2', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저2', 'user2@gmail.com', '2000-10-29'), ('user3', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저3', 'user3@gmail.com', '1995-09-29'), ('user4', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저4', 'user4@gmail.com', '1999-09-29'), ('user5', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저5', 'user5@gmail.com', '2008-09-29'), ('user6', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저6', 'user6@gmail.com', '2007-09-29'), ('user7', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저7', 'user7@gmail.com', '2006-09-29'), ('user8', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저8', 'user8@gmail.com', '2015-09-29'), ('user9', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저9', 'user9@gmail.com', '2012-09-29'), ('user10', '$2a$10$8VKmqNwV0x/bQEN8Z54w7uUpLPTGeHgoJR73dyH2S6ZxoHkVkxGSm', '유저10', 'user10@gmail.com', '2008-09-29'); -- tbl_roles 데이터 삽입 INSERT INTO tbl_roles (name) VALUES ('USER'), ('ADMIN'), ('MANAGER'), ('SUPPORT'); -- tbl_user_roles 데이터 삽입 (USER 역할 할당) INSERT INTO user_roles (user_id, role_id) VALUES (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1), (9, 1), (10, 1); -- tbl_todos 데이터 삽입 INSERT INTO tbl_todos (user_id, title, description, target_date, done) VALUES (1, '첫 번째 할 일', '첫 번째 할 일', '2024-09-30', false), (1, '두 번째 할 일', '두 번째 할 일', '2024-10-01', false), (1, '세 번째 할 일', '세 번째 할 일', '2024-10-02', true), (1, '네 번째 할 일', '네 번째 할 일', '2024-10-03', false), (1, '다섯 번째 할 일', '다섯 번째 할 일', '2024-10-04', true), (1, '여섯 번째 할 일', '여섯 번째 할 일', '2024-10-05', false), (1, '일곱 번째 할 일', '일곱 번째 할 일', '2024-10-06', true), (1, '여덟 번째 할 일', '여덟 번째 할 일', '2024-10-07', false), (1, '아홉 번째 할 일', '아홉 번째 할 일', '2024-10-08', true), (1, '열 번째 할 일', '열 번째 할 일', '2024-10-09', false), (2, '할 일 11', '할 일 11', '2024-10-10', true), (2, '할 일 12', '할 일 12', '2024-10-11', false), (2, '할 일 13', '할 일 13', '2024-10-12', true), (2, '할 일 14', '할 일 14', '2024-10-13', false), (2, '할 일 15', '할 일 15', '2024-10-14', true), (2, '할 일 16', '할 일 16', '2024-10-15', false), (2, '할 일 17', '할 일 17', '2024-10-16', true), (2, '할 일 18', '할 일 18', '2024-10-17', false), (2, '할 일 19', '할 일 19', '2024-10-18', true), (2, '할 일 20', '할 일 20', '2024-10-19', false), (3, '할 일 21', '할 일 21', '2024-10-20', true), (3, '할 일 22', '할 일 22', '2024-10-21', false), (3, '할 일 23', '할 일 23', '2024-10-22', true), (3, '할 일 24', '할 일 24', '2024-10-23', false), (3, '할 일 25', '할 일 25', '2024-10-24', true), (3, '할 일 26', '할 일 26', '2024-10-25', false), (3, '할 일 27', '할 일 27', '2024-10-26', true), (3, '할 일 28', '할 일 28', '2024-10-27', false), (3, '할 일 29', '할 일 29', '2024-10-28', true), (3, '할 일 30', '할 일 30', '2024-10-29', false), -- (나머지 할 일 데이터 계속해서 추가...) (4, '할 일 31', '할 일 31', '2024-10-30', true), (5, '할 일 41', '할 일 41', '2024-11-09', true), (6, '할 일 51', '할 일 51', '2024-11-19', true), (7, '할 일 61', '할 일 61', '2024-11-29', true), (8, '할 일 71', '할 일 71', '2024-12-09', true), (9, '할 일 81', '할 일 81', '2024-12-19', true), (10, '할 일 91', '할 일 91', '2024-12-29', true);
댓글 ( 0)
댓글 남기기