업무 시스템에서의 역할 기반 접근 제어

RBAC의 토대


업무 시스템의 권한 제어의 기본 형식은 역할 기반 액세스 제어(RBAC)이다.단순화하면 다음과 같은 모형이다.
  • Subject(시스템 사용자)에는 여러 Role(역할)이 있습니다.
  • Role(역할)은 Permission(권한)의 그룹으로 구성됩니다.
  • Permission(권한)은 일련의 조작(허용된 조작)으로 구성
  • 구체적으로 Redmine의 예를 살펴보겠습니다.
  • 관리자, 개발자, 보고서 역할을 기본적으로 사용자에게 할당할 수 있습니다.
  • 리포터 역할에 Add Issues 권한이 있습니다.
  • Add Issues 권한이 있는 사용자는 새 Issue를 작성할 수 있습니다.
  • 이 모델은 Redmine에서 다음과 같이 표현됩니다.

    Redmine은 한 사용자가 여러 항목에서 서로 다른 역할을 여러 항목에 할당할 수 있기 때문에 상기 Member 실체를 중간에 끼우는 모델입니다.RBAC의 원칙을 조금만 적용하면 복잡한 업무 요구에 적응할 수 있다.
    권한 테이블을 찾을 수 없지만, 쉼표를 볼륨 테이블의permissions 열로 구분합니다.이른바 SQL 반모드란 제이워커의 디자인이다.이것은 Redmine 플러그인이 자유롭게 권한을 추가할 수 있다는 핑계일 수 있습니다.
    RBAC는 매우 합리적인 디자인으로 보이지만 실제로 이런 디자인의 업무 시스템을 본 사람은 드물다.조직=캐릭터,캐릭터=권한의 디자인, 아시는 분 계시죠?또 프레임 작업도 RBAC를 잘 지원하는 사람이 드물다.

    RBAC를 설계하지 않을 때의 문제


    조직


    나는 이것이 좋지 않은 이유라고 생각하지만, 조직은 빈번하게 변화한다.이것을 볼륨으로 삼으면 매번 대량의 데이터 유지보수가 필요하다.많은 손님들이 조직명으로 캐릭터성을 요구하지만 반드시 캐릭터를 따로 디자인하고 조직과 비추어야 한다.

    역할 = 권한


    캐릭터에 해당하는 유형은 없다.사용자에 대한 권한 분배는 거대해지기 쉽다.작업 권한의 집합을 찾을 수 없기 때문에 C2 덮어쓰기를 충족시키려면 권한이 있는 조합 테스트를 해야 합니다.

    권한 = 작업


    하나의 권한을 사용하여 여러 작업을 제어할 수 있습니다.다음 설명에서 권한 A가 있으면 URL에 접근할 수 있을 뿐만 아니라 링크 단추를 분리해서 사용할 수 있다.권한 제어를 원하는 동작을 그룹으로 나누어 권한으로 설계합시다.

    Java에서 설치


    서브렛 API에는 비즈니스 응용프로그램에서도 Java를 사용할 수 있는 작동 메커니즘이 있습니다.하지만.. 캐릭터를 유연하게 활용해야 하는 경우가 많다.관리자는 더 잘하기를 원하고 해소할 필요조건도 많다.이렇게 되면 JSR250과 서브렛 API처럼 캐릭터 이름을 가이드에 붙이거나 isUserInRole처럼 파라미터ロール名를 파라미터에 넘기는 것이 너무 딱딱해서 상황이 좋지 않다.RBAC의 정의에 따라 권한이 부여됩니다.
    그래서 우리는 Ninjaframework를 바탕으로 캐릭터명이 아닌 권한명을 사용한 응용 프로그램 Example을 제작해 보았다.

    Example 설명


    초기 데이터로서 3가지 역할을 준비했으며 다음과 같은 권한을 가진다.
    롤러
    사용 권한
    Administrator
    readIssue, writeIssue, manageUser
    Developer
    readIssue, writeIssue
    Guest
    readIssue

    로그인 시 권한 얻기


    로그인할 때 사용자의 역할에서 권한을 얻고 사용자 정보와 함께 저장하는 것이 현실적인 디자인이다.따라서 세션의 DTO를 다음과 같이 로그인 정보로 설치했습니다.
    @Data
    @RequiredArgsConstructor
    public class UserPrincipal implements Principal {
        @NonNull
        private String name;
        @NonNull
        private Set<String> permissions;
    }
    
    로그인할 때 이것을 권한으로 변환하고 세션을 소유합니다.
    User user = em
        .createQuery("SELECT u FROM User u LEFT JOIN FETCH u.roles r LEFT JOIN FETCH r.permissions p WHERE u.account = :account", User.class)
        .setParameter("account", account)
        .getSingleResult();
    
        session.put("account", account);
        Set<Permission> permissions = new HashSet<>();
        user.getRoles().stream()
            .map(Role::getPermissions)
            .forEach(permissionsInRole -> permissions.addAll(permissionsInRole));
    
        session.put("permissions", permissions.stream().map(Permission::getName)
            .collect(Collectors.joining(",")));
    
    Ninjaframe work의 세션은 쿠키 저장소이기 때문에 위와 같은 문자열만 넣는다(이것은 Ninjaframe work를 사용하지 않는 이유이다).

    방법 권한


    이렇게 하면 컨트롤러 방법에 할당될 때 가지고 있는 권한과 동작 방법에 첨부된 권한을 검사합니다.
    인증 필터를 사용하여 세션에서 UserPrincipal을 복원합니다.
    Session session = context.getSession();
    if (session.get("account") == null) {
        return Results.redirect("/login");
    }
    Set<String> permissions = new HashSet<>();
    
    if (session.get("permissions") != null) {
        permissions = ImmutableSet.copyOf(Splitter.on(',').split(session.get("permissions")));
    }
    context.setAttribute("principal", new UserPrincipal(session.get("account"), permissions));
    
    그런 다음 승인된 필터로 UserPrincipal에 접근할 수 있는 권한이 있는지 확인하면 됩니다.디렉터 메서드에는 다음 JSR250 파생 파일에 필요한 권한이 적혀 있습니다.
        @UnitOfWork
        @RolesAllowed("readIssue")
        public Result list(Context context) {
            EntityManager em = entityManagerProvider.get();
            List<Issue> issues = em
                    .createQuery("SELECT i FROM Issue i", Issue.class)
                    .getResultList();
    
            Map<String, Object> bindings = VariablesHelper.create(context);
            bindings.put("issues", issues);
            return Results.html().render(bindings);
        }
    
        @UnitOfWork
        @RolesAllowed("writeIssue")
        public Result newIssue(Context context) {
            return Results.html().render(VariablesHelper.create(context));
        }
    
    승인된 검사는 다음과 같습니다.
    Method method = context.getRoute().getControllerMethod();
    RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
    PermitAll permitAll = method.getAnnotation(PermitAll.class);
    
    UserPrincipal principal = (UserPrincipal) context.getAttribute("principal");
    
    if (permitAll != null ||
            (rolesAllowed != null && contains(principal.getPermissions(), rolesAllowed.value()))) {
        return filterChain.next(context);
    } else {
        return Results.forbidden().html().template("views/403forbidden.ftl.html");
    }
    
    따라서 위에서 말한 바와 같이 컨트롤러 방법에 개편만 더하면 된다.

    뷰 레이어 사용 권한


    권한의 사용 방법은 이것뿐만 아니라 메뉴와 단추의 분리도 사용하세요.이것도 User Principal에서 필요한 권한이 있는지 확인한 다음 보기에서 구분할 수 있습니다.
    Ninjaframe work의 기본 보기는 Fremarker이기 때문에 다음과 같은 내용을 쓸 수 있습니다.
    <a href="#" class="header item">
        RBAC Example
    </a>
    <#if principal??>
        <a href="/" class="item">Home</a>
    </#if>
    <#if principal?? && principal.permissions?seq_contains("readIssue")>
        <a href="/issues/" class="item">Issue</a>
    </#if>
    <#if principal?? && principal.permissions?seq_contains("manageUser")>
        <a href="/users/" class="item">Users</a>
    </#if>
    
    Admin 권한이 있는 경우

    "Admin"권한이 없는 경우

    이렇게 나눠졌어요.

    Example 이동 방법

    % git clone https://github.com/kawasima/rbac-example.git
    % cd rbac-example
    % mvn compile
    % mvn waitt:run
    
    Example 응용 프로그램을 시작할 수 있습니다.
    아, 이waitt 플러그인은 스프링부트를 사용하지 않아도 IDE의 유상 기능을 사용하지 않고 웹 앱을 포장하지 않아도 시작할 수 있다2015년 주목받은 플러그인.꼭 해보세요.

    Spring security


    Spring security를 사용하면 RBAC를 설치할 수 있습니다.잡지의 상황 때문에, 내가 사랑을 끊는 것을 허락해 주십시오.

    총결산


    비록 권한 제어는 업무 시스템에서 가장 빈번한 필수 조건이지만 디자인 방법을 충분히 설명하는 것을 찾기 어렵고 현실에는 디자인 테스트와 운용이 어려운 시스템이 많다.이 보도가 그 설계에 조금이나마 도움이 되었으면 합니다.

    좋은 웹페이지 즐겨찾기