아래 테이블과 같이 자원에 접근한 권한이 있다고 치자.
| 권한 | 접근가능자원(url) |
|---|---|
| ROLE_ADMIN | /role_admin, /role_manager, /role_user |
| ROLE_MANAGER | /role_manager, /role_user |
| ROLE_USER | /role_user |
admin role이 user, manager role이 가진 권한을 포함한다는 것을 쉽게 파악할 수 있다.
스프링 시큐리티에선 이런 계층적인 권한을 어떻게 정의할 수 있을까?
AuthorityAuthorizationManager도 RoleHierachy 인터페이스를 사용한다. @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을 사용하였다.
@Bean
public RoleHierarchy roleHierarchy(){
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(
roleService.getRoleHierarchyInfo()
);
return roleHierarchy;
}
Spring security의 인가매니저가 사용할 수 있도록, Role hierarchy Bean을 등록해준다.
지난 작성글에서 찾아본 AuthorityAuthorizationManager도 RoleHierarchy를 사용한다.
RoleHierarchyImpl 은 setHierarchy를 지원한다. 아까 위에서 봤던 ROLE_A>ROLE_B와 같은 문자열을 집어넣으면 Role 계층을 설정해주는 메서드이다.
ROLE_A>ROLE_B처럼 role의 계층관계를 표현하는 문자열을 의미한다. RoleHierarchyImpl이 이 문자열을 받아서 Role 계층관계를 구현한다. 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();
}