티스토리 뷰

안녕하세여

 

기존에는 메뉴를 고정적으로 사용했는데요 (유연성없이 FO에서 메뉴를 고정적으로 하드코딩함)

 

시큐리티에서 메뉴에 대한 접근권한을 나누다보니 사용자마다 권한에 따른 메뉴를 표기해야하는 경우가 생겼습니다

기존에 사용자마다 role이 지정되어있기 때문에 

public enum UserRole {
    ADMIN,
    MANAGER,
    DEVELOPER,
    USER;
}

 

이 부분을 참고하여 Menu를 만들어보려고 해용

 

 

 

@Entity
@Table(name = "menu")
@Getter
@NoArgsConstructor
public class Menu extends BaseTimeEntity {

    @Id
    @GeneratedValue
    private Long id;

    @Column(nullable = false, length = 255)
    private String name;

    @Column(nullable = true, length = 255)
    private String path;

    @ElementCollection(targetClass = UserRole.class, fetch = FetchType.EAGER)
    @CollectionTable(name = "menu_roles", joinColumns = @JoinColumn(name = "menu_id"))
    @Enumerated(EnumType.STRING)
    @Column(name = "role")
    private Set<UserRole> roles = new HashSet<>(); // 접근 가능한 역할 목록


    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_id") // 계층 구조를 위한 상위 메뉴
    private Menu parent;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Menu> children = new ArrayList<>();

    @Column(nullable = false)
    private int menuOrder;

    @Column(nullable = true)
    private boolean isEnabled;

    @Builder
    public Menu(String name, String path, Set<UserRole> roles, Menu parent, List<Menu> children, int menuOrder, boolean isEnabled) {
        this.name = name;
        this.path = path;
        this.roles = roles;
        this.parent = parent;
        this.children = children;
        this.menuOrder = menuOrder;
        this.isEnabled = isEnabled;
    }
}

 

메뉴 생성까지 생각해서 빌더 어노테이션을 달아주었구요

부모메뉴, 자식메뉴가 있을 수 있어 (추후 메뉴 트리구조로 변환가능성 있음) 

parent, children까지 추가해주었네요

권한은 중복값이 들어가면 안되니 HashSet으로 구현해주었답니다

 

테스트 개발계에 계정 여러개 중에

developer랑 administrator 를 만들어두었습니다

developer:  "DEVELOPER"

administrator: "ADMIN"

의 권한을 가지고 있습니다.

 

Service에서 본인의 권한에 맞는 메뉴를 가지고와 Controller에게 반환해주는 작업을 했어요

(선행작업으로는 MENU를 생성하는 것이 먼저입니다 ^^*)

 

public List<Menu> findAllByOrderByMenuOrder(){
        // 스프링 시큐리티에서 현재 인증된 사용자 정보 가져오기
        Set<UserRole> currentUserRoles = getCurrentUserRoles();  // 로그인한 사용자의 역할 가져오기

        // 모든 메뉴를 가져온 후, 사용자의 역할에 맞는 메뉴만 필터링
        List<Menu> allMenus = menuRepository.findAllByOrderByMenuOrder();
        return allMenus.stream()
                .filter(menu -> menu.getRoles().stream().anyMatch(role -> currentUserRoles.contains(role)))
                .collect(Collectors.toList());
    }
    
// 현재 사용자의 권한 가져오기
    public Set<UserRole> getCurrentUserRoles() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication != null && authentication.isAuthenticated()) {
            return authentication.getAuthorities().stream()
                    .map(GrantedAuthority::getAuthority)  // 권한 문자열
                    .map(role -> role.replace("ROLE_", ""))  // 'ROLE_' 접두사를 제거
                    .map(UserRole::valueOf)               // 문자열을 UserRole enum으로 변환
                    .collect(Collectors.toSet());
        }
        return Set.of(); // 인증되지 않은 경우 빈 Set 반환
    }

    public static Set<UserRole> validateRoles(List<String> roleNames) {
        Set<UserRole> validRoles = new HashSet<>();

        for (String roleName : roleNames) {
            try {
                // UserRole Enum에 해당하는지 확인
                UserRole role = UserRole.valueOf(roleName);
                validRoles.add(role);
            } catch (IllegalArgumentException e) {
                // 유효하지 않은 역할인 경우 처리
                log.error("Invalid role: " + roleName);
            }
        }
        return validRoles;
    }

 

 

menu order순으로 조회해서 반환해줘야하구 로그인(인증)된 사용자의 권한에 맞는 메뉴를 리턴해줘야하니

위와 같은 코드를 짜게 되었는데요

 

DB에는 enum값으로 저장이 되어있는데

User 도메인을 보면  아래와 같이 ROLE_ 이라는 접두사를 사용하고 있어요

이는 시큐리티 코드에서 메뉴별 접근권한을 나눌 때 hasRole api를 사용하고 있어서 그렇습니다.

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(new SimpleGrantedAuthority("ROLE_" + userRole.name()));
    }

 

그래서 "ROLE_" 을 ""로 치환해주는 replace함수를 넣어줬어요

이를 set객체로 반환해주고용

 

map함수를 통해 사용자 권한이 포함된 메뉴만 List로 반환해주는 메서드를 작성했습니다

 

으아아아아아아ㅏ앙아

첨에는 어떻게 해야할까 고민이 많았는데

차근차근하니 어찌저찌 해결이 되어가고 있습니다

 

끝..

이제 30분 쉬러갈래요...