Role hierarchy

rejs·2024년 3월 8일

Role 계층을 만드는 방법

아래 테이블과 같이 자원에 접근한 권한이 있다고 치자.

권한접근가능자원(url)
ROLE_ADMIN/role_admin, /role_manager, /role_user
ROLE_MANAGER/role_manager, /role_user
ROLE_USER/role_user

admin role이 user, manager role이 가진 권한을 포함한다는 것을 쉽게 파악할 수 있다.

스프링 시큐리티에선 이런 계층적인 권한을 어떻게 정의할 수 있을까?

RoleHierachy

  • RoleHierachy를 사용하여 구현할 수 있다. 이전 포스트에서 봤던 AuthorityAuthorizationManager도 RoleHierachy 인터페이스를 사용한다.

데이터베이스에서 정보가져와 RoleHierachy 구현하기

1단계. Role Entity

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "roles")
public class Role {
    @Id
    @Column(name = "role_name", unique = true)
    @Enumerated(EnumType.STRING)
    private RoleName roleName;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "parent_role")
    private Role parentRole;

	/*생략*/
}

role Name은 enum을 사용하였다.

  • self join을 사용하여 role간의 계층 정보를 표현하였다.
    chile_role이 parent_role의 권한을 포함한다고 생각하고 작성하였다. (객체의 상속처럼)
  • entity에 childRole을 mapping해주지 않은 이유는 role 계층관계 구현에서 parent_role이 child_role을 검색할 이유가 없기 때문에 추가하지 않았다.

2단계. Role hierarchy Bean 등록

    @Bean
    public RoleHierarchy roleHierarchy(){
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy(
            roleService.getRoleHierarchyInfo()
        );
        return roleHierarchy;
    }
  • Spring security의 인가매니저가 사용할 수 있도록, Role hierarchy Bean을 등록해준다.
    지난 작성글에서 찾아본 AuthorityAuthorizationManagerRoleHierarchy를 사용한다.

  • RoleHierarchyImpl 은 setHierarchy를 지원한다. 아까 위에서 봤던 ROLE_A>ROLE_B와 같은 문자열을 집어넣으면 Role 계층을 설정해주는 메서드이다.

3단계. roleHierarchyStringRepresentation 작성

  • ROLE_A>ROLE_B처럼 role의 계층관계를 표현하는 문자열을 의미한다. RoleHierarchyImpl이 이 문자열을 받아서 Role 계층관계를 구현한다.
  • RoleService에 구현하였다.
    public  String getRoleHierarchyInfo(){
        List<Role> roles = roleRepository.findChildRole();
        StringBuilder sb = new StringBuilder();
        for(Role childRole : roles){
            String childRoleName = childRole.getRoleName().toString();
            String parentRoleName = childRole.getParentRole().getRoleName().toString();

            /*
               Child가 Parent보다 더 많은 권한을 보유함
             */
            sb.append(childRoleName);
            sb.append(" > ");
            sb.append(parentRoleName);
            sb.append("\n");
        }
        return sb.toString();
    }
  • 객체의 상속관계처럼 child role이 parent role의 권한을 상속받는다고 생각하고 parentRole, childRole이라는 용어를 사용하였다.

  • 덤으로 RoleRepository는 아래처럼 작성하였다.

@Repository
public interface RoleRepository extends JpaRepository<Role,Long> {
	/* 생략 */
    @Query("select r from Role r join fetch r.parentRole pr 
    	where r.parentRole is not null")
    List<Role> findChildRole();
}

0개의 댓글