스프링

 

 

 

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);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

지식에는 두 가지 종류가 있다. 하나는 우리가 어떤 주제에 대해 직접 아는 것이고, 다른 하나는 정보나 지식이 있는 곳을 알고 있는 것이다. -새뮤얼 존슨

댓글 ( 0)

댓글 남기기

작성