Zum Hauptinhalt springen

역할 기반 접근 제어 (RBAC)

1. 개요

Lumie 백엔드는 두 계층의 접근 제어를 사용합니다.

  1. Role 계층: libs/commonRole 열거형으로 정의되는 수직 권한 계층 — JWT role claim으로 전파
  2. Permission 코드: staff 모듈의 permissions 테이블에 저장되는 세분화된 기능 접근 권한 — 직원(staff)별로 AccessLevel(NONE / READ / WRITE) 부여

2. Role 정의

Role 열거형은 libs/common/src/main/java/com/lumie/common/auth/Role.java에 위치합니다. 모든 모듈이 auth 모듈에 의존하지 않고도 역할을 참조할 수 있도록 공용 라이브러리에 있습니다.

// Role.java — libs/common
public enum Role {
OWNER(0), // 원장 — 학원 전체 최고 권한자
MANAGER(1), // 부원장 — 위임된 범위 관리
INSTRUCTOR(2), // 강사 — 담당 반 내 운영
STUDENT(3); // 학생 — 본인 정보 조회

/**
* @return true if this role has equal-or-higher authority than required
* (OWNER.hasAuthority(INSTRUCTOR) == true)
*/
public boolean hasAuthority(Role required) {
return this.level <= required.level;
}

/**
* @return true if this role strictly outranks target
* (OWNER.canManage(MANAGER) == true, INSTRUCTOR.canManage(INSTRUCTOR) == false)
*/
public boolean canManage(Role target) {
return this.level < target.level;
}
}

숫자가 낮을수록 상위 권한입니다. hasAuthority는 상위 역할이 하위 역할의 권한을 자동 포함합니다.


3. 토큰 전파

Role은 JWT 토큰의 role claim에 포함되어 모든 요청에 전파됩니다. JwtAuthenticationFilterUserContextHolder.setUserRole(claims.role().name())을 호출하여 스레드 로컬에 저장합니다.

// 서비스 레이어에서 현재 역할 조회
Role currentRole = UserContextHolder.getUserRole();

// 권한 확인 예시 — MANAGER 이상만 허용
if (!currentRole.hasAuthority(Role.MANAGER)) {
throw new BusinessException(CommonErrorCode.FORBIDDEN);
}

// OWNER 전용 작업
AuthorizationGuard.requireOwner(); // libs/common 유틸 — OWNER 아니면 FORBIDDEN

4. 직원 등록 가능 역할

직원 등록 API(staff 모듈)는 MANAGER, INSTRUCTOR 만 생성할 수 있습니다. OWNER는 테넌트 가입 시 시스템이 자동 생성합니다.

// StaffCommandService.java
private static final EnumSet<Role> CREATABLE_ROLES = EnumSet.of(Role.MANAGER, Role.INSTRUCTOR);

등록자 역할이 생성 대상 역할을 canManage(targetRole) 로 검사합니다. 동급 역할 생성이 차단됩니다.


5. 세분화된 퍼미션 (staff 모듈)

직원 단위의 세분화된 기능 접근 권한은 permissions 테이블과 staff_permissions 조인 테이블로 관리됩니다.

// AccessLevel.java — staff 모듈
public enum AccessLevel {
NONE, READ, WRITE
}

StaffService.getStaffPermissions(tenantSlug, staffId) 는 직원의 퍼미션 코드 목록을 반환합니다.

record StaffPermissionsData(
Long staffId,
List<PermissionEntry> permissions // {permissionCode, accessLevel}
) {}

퍼미션 코드와 카테고리는 permissions.code, permissions.category 컬럼으로 테넌트 스코프 내에서 관리됩니다.


6. Spring Security 엔드포인트 인가

현재 SecurityConfig는 경로 레벨에서 ROLE_INTERNAL(내부 worker 콜백) 외에 별도의 role 기반 인가 규칙을 선언하지 않습니다. 세분화된 접근 제어는 서비스 레이어에서 UserContextHolder.getUserRole()Role.hasAuthority() / AuthorizationGuard.requireOwner() 를 통해 구현합니다.

// SecurityConfig.java — 주요 인가 규칙
.requestMatchers("/internal/**").hasRole("INTERNAL") // HMAC 인증 경로
.anyRequest().authenticated() // 나머지는 JWT 인증만 요구

7. Frontend 참고사항

  • RoleSchema: entities/session/model/schema.ts
  • useIsAdmin(): OWNER, MANAGER, INSTRUCTOR를 관리자측으로 판별
const ADMIN_ROLES = ['OWNER', 'MANAGER', 'INSTRUCTOR'];

관련 문서