CASL.js

소개

  • 지정된 클라이언트가 액세스할 수 있는 리소스를 제한하는 JavaScript 동형 인증 라이브러리
  • User Action

    • 사용자가 APP에서 실제로 수행할 수 있는 작업을 설명, 비즈니스 로직에 따라 단어로 정의(일반적으로는 동사) (ex. prolong, CRUD 형식인 create, read, update, delete)
  • Subject

    • 사용자 작업을 확인하려는 제목 또는 제목의 유형, 일반적으로 비즈니스 또는 도메인 entity
  • Fields

    • 일치하는 Subject’s fields 로만 사용자 작업을 제한하는 데 사용할 수 있습니다. (ex. status field를 업데이트 할 수 있도록 허용하고 description 또는 title의 경우 업데이트를 허용하지 않을 수 있습니다.)
  • Conditions

    • 일치하는 subject에만 사용자 작업을 제한하는 조건 기준입니다. 특정 subject에 대한 권한을 부여해야 할 때 유용하게 사용

규칙 정의

  • defineAbility

    • can, cannot 인자를 넘겨줌
    • can, cannot Parameter

      • action: string | string[]
      • subjectType: string | Function
      • fields: string[]
      • conditions: Conditions
    import { defineAbility } from '@casl/ability';
    
    export default defineAbility((can, cannot) => {
    can('read', 'Post');
    cannot('delete', 'Post', { published: true });
    });
    • 주 사용처
    • 유닛 테스트
    • 예제 및 학습 자료
    • 간단한 어플리케이션 이나 프로토타입
  • AbilityBuilder Class

    import { AbilityBuilder, Ability } from '@casl/ability';
    
    const { can, cannot, build } = new AbilityBuilder(Ability);
    
    can('read', 'Post');
    cannot('delete', 'Post', { published: true });
    
    export default build();
    • 주 사용처
    • 정적 권한이 있는 어플리케이션

Vue에서 활용(Vue 3.x)

  • npm install @casl/vue @casl/ability
  • main.js에 import 시 Vue component 내 에서도 전역으로 호출 가능
import ability from './config/ability';
import { abilitiesPlugin } from '@casl/vue';

Vue.use(abilitiesPlugin, ability, {
  useGlobalProperties: true,
});
<template>
  <div v-if="$can('create', 'all')"></div>
</template>

Can Component

  • Can Component import 시 Component로 특정 element 및 component에 권한 지정가능
import ability from './config/ability';
import { Can, abilitiesPlugin } from '@casl/vue';

Vue.use(abilitiesPlugin, ability);
Vue.component(Can.name, Can);
<template>
  <Can I="create" a="Post">
    <a @click="createPost">Add Post</a>
  </Can>
</template>
  • Can component의 경우 5개의 property 사용가능

    • do - action의 이름 (ex. read, update) alias: I
    • on - subject 체크 alias: a, an, this
    • field - field 체크
    • not - 사용자가 어떤 action을 수행할 수 없는 경우 ability 체크를 반전하고 UI 표기
    • passThrough - 사용자의 권한에 따라 특정 element를 비활성화 해야하는 경우 등에 사용
export default [
  {
    path: '/404',
    name: '404',
    component: () => import('@/views/error/Error404.vue'),
    meta: {
      layout: 'full',
      resource: 'Auth', // beforeEach에서 라우팅 이전 권한체크 ability에 subject에 해당
      action: 'read', // beforeEach에서 라우팅 이전 권한체크 ability에 action에 해당
    },
  },
  {
    path: '/not-authorized',
    name: 'not-authorized',
    component: () => import('@/views/error/NotAuthorized.vue'),
    meta: {
      layout: 'full',
      resource: 'Auth',
    },
  },
];
  • router/index.js
router.beforeEach((to, _, next) => {
  const isLoggedIn = isUserLoggedIn();
  if (!canNavigate(to)) {
    /* canNavigate 함수를 통해 route에 선언된 meta내의 action, resource 값을 통해 권한 부여
 가능여부를 체크 */
    // 미 로그인 시 로그인 페이지로
    if (!isLoggedIn) return next({ name: 'auth-login' });
    // 권한 없을 시 not-authorized 페이지로 이동
    return next({ name: 'not-authorized' });
  }

  if (to.meta.redirectIfLoggedIn && isLoggedIn) {
    const userData = getUserData();
    next(getHomeRouteForLoggedInUser(userData ? userData.role : null));
  }

  return next();
});

React 에서의 활용

  • Document
  • 설치

    npm install @casl/react @casl/ability
    # or
    yarn add @casl/react @casl/ability
  • (작성중) 사용

    • provider 로 컴포넌트 감싸기
    • AbilityContext.Provider
    • value에 ability 인스턴스 넘겨주기
  • (작성중) 자재플랫폼 적용

    • _app.tsx
    • provider
    • ability 인스턴스 생성하여 layout 컴포넌트에 전달
    • 유저정보를 받는 시점을 어디에서 할지..? ← 변경될 가능성

      • 현재는 Layout 컴포넌트에서 인스턴스 재할당
      • 이때 permissions에서 정의한 규칙 사용
    • permissions.ts
    • AbilityBuilder 를 사용하여 역할별 권한 부여
    • layout
    • layout의 자식컴포넌트인 navbar 와 sidebar에 인스턴스를 전달
    • 전달받은 인스턴스를 사용하여 사이드바 노출 제어
    • (작업필요) 라우트 처리?