서비스 접근 제어는 권한을 가진 사용자가 서비스를 사용할 수 있도록 마련한 장치를 말합니다. 이번 글에서는 진행했던 프로젝트에서 이러한 것을 설계하고 구현한 내용을 이야기 해볼까 합니다.

먼저 이 글에서 서비스라 함은 잘~ 정의된 웹 상에서 유일하게 식별되는 자원, 웹 서비스를 말합니다. 프로젝트에서 개발한 시스템은 수십~수백 개가 넘는 서비스를 제공하고 있었습니다. 그리고 이 서비스를 사용하는 클라이언트는 매우 다양 했습니다. 다양한 사용자의 웹 브라우저, 단말기 또는 또 다른 시스템이기도 했지요...

이처럼 다양한 클라이언트가 다양한 서비스를 사용하기 때문에... 고객들은 당연하게도!!  이를 관리하고 싶어했습니다.

그리하여 고객이 원하던 모습은...

시스템이 제공하는 모든 서비스를 한눈에 보고싶다. 서비스명, URI와 기타 HTTP 프로토콜 정보들... 매핑되는 소스코드 클래스명, 메서드, 입력 값이 어떻게 되고, 어떤 응답이 나오는지...(고객들이 기술적인 이해도가 깊어 자세히 들여다보고 싶어 하더군요... 심지어 어떤 컴포넌트, 어떤 비즈니스 계층을 거쳐 데이터 접근 영역까지 소스 코드가 호출되는 계층 구조 까지도 관리페이지에서 한눈에 보고 싶어 했습니다.)

클라이언트마다 사용할 수 있는 서비스가 다르다. 어떤 서비스는 이용 가능하게 하고 어떤 서비스는 이용 못하도록 제한을 둘 수 있어야 한다.(사용자의 등급, 권한 별로 이용할 수 있는 서비스를 차별화 하고 싶은 것이었습니다. )

시스템 운영중에 실시간으로 클라이언트별 접근 가능한 서비스를 바꿀 수 있어야 한다. (사용자들에게 어떤 서비스는 사용 가능하도록 열어주고, 어떤 서비스는 차단하도록 실시간으로 바꿀 수 있어야 했습니다. - 예를 들어 블랙리스트 사용자는 차단)

그래서 위의 요구 사항들을 만족하기 위해 실시간 서비스 접근제어를 위한 개발을 진행했습니다.

큰 구조는 다음과 같습니다.

서비스 접근제어 구조 설계

위의 그림에서 보면 서비스 자원 객체를 컨트롤러로 보면 됩니다. 사용자의 서비스 요청이 발생하면 컨트롤러에 진입 하기 전 가로채어(Intercept) 사용자의 서비스접근 가능여부를 판단하고 진행합니다. 접근이 불가하면 해당하는 예외를 발생하여 적절한 예외 메시지를 클라이언트에게 응답으로 전송하도록 하였습니다.

서비스 등록

위에서 언급한 고객의 요구사항 중 1번을 위해서는 어딘가에 모든 서비스 목록을 등록해야 합니다. 관리자가 서비스 관리 페이지에서 수동으로 일일히 등록할 수도 있겠습니다만 수십~수백개의 서비스와 부가 정보를 수작업으로 한다면 매우 불편하기도 하고, 제 경함상 시간이 흐르면서 점점 실제와 동기화 되지 않을 가능성이 매우 큽니다.^^

그래서 서비스 목록을 추출하기 위해 실제 웹서버에 배포되는 WAR파일을 직접 분석하기로 결정 했습니다.

관리자의 유스케이스는 매우 간단합니다.

관리자는 서비스 관리 페이지에서 빌드된 WAR 파일을 선택 - 직접 파일 업로드 혹은 통합 레파지토리(Nexus와 같은...)와 연결 - 해서 동기화 버튼을 선택한다.

시스템은 관리자가 선택한 WAR 파일을 분석해서 버전정보, 제공하는 서비스 목록 및 디테일한 정보들(서비스명, URI, HTTP메서드, 기타 프로토콜 정보, 처리하는 자바 클래스, 메서드명, 입력/응답 타입, 예외, 부가적으로 하위 계층까지의 소스코드 호출구조 까지)를 화면에 보여준다.

서비스 분석 및 등록UI

위 그림은 서비스 목록을 분석하고 보여주기 위한 관리자 UI 입니다. WAR가 변경된 경우 변경된 파일을 선택하여 동기화 하거나 직접 업로드 할 수 있습니다. 하단에 보이는 목록은 분석된 서비스 목록입니다.

WAR 파일 내에는 Java byte code와 의존관계에 있는 다른 컴포넌트, 외부 라이브러리들 그리고 기타 파일(jsp, js, properties 등등)들이 존재합니다. 이를 분석하여 제공하는 서비스 목록을 추출합니다.

[참고]

Java byte code를 분석하기 위해서는 ASM이라는 라이브러리를 사용 했습니다. 기회가 되면 ASM 관련된 내용도 올리겠습니다.(Java 소스 파일이 아닌 컴파일 된 byte code를 직접 분석하는 과정도 흥미롭습니다.^^ 미리 관심 있으신 분들은 http://asm.ow2.org/ 를 참조 바랍니다.)

저희는 웹 프레임워크로 Spring  @MVC를 사용 했었는데 Java byte code를 분석하면서 웹 서비스를 제공하는 컨트롤러에 명시한 Spring @MVC어노테이션과 사용자 정의 어노테이션을 보고 서비스 목록을 추출 했습니다.

분석하는 Java byte code의 실제 소스코드는 다음과 같습니다.(분석 시 @Controller, @RequestMapping, @RestInterface 등의 어노테이션을 보고 정보를 추출하게 됩니다.)

Java byte code의 실제소스코드

서비스 구성 - Feature와 Product

수많은 서비스를 개별적으로 접근 가능 여부를 지정한다면 이를 관리하는 것도 매우 번거로운 작업일 것입니다. 따라서 서비스를 편리하고 자유롭게 구성하기 위해 다음과 같은 모델을 설계 했습니다.

  • Feature(피쳐) - 서비스를 그룹핑하는 단위 입니다. 개별 서비스를 구성하는데 정해진 룰은 없지만 주로 관련된 서비스들로 묶이게 될 것입니다.
  • Product(상품) - 최종적으로 사용자가 구독(Subscription)하게 되는 단위 입니다. 여러 Feature를 선택하여 Product를 만들게 됩니다. 예를 들면 요금을 많이 내는 사용자와 그렇지 않은 사용자들에게 차별화된 서비스를 제공하기 위해 서로 다른 Feature를 선택하여 요금을 많이 내는 사용자가 이용할 수 있는 상품, 일반 상품 등을 만들어 내는 것입니다.

사용자 그룹 - Community와 Club

사용자 그룹은 말 그대로 입니다. 어떤 그룹에 속하기 위해 가입 신청이라는 프로세스를 거칠 수도 있고, 관리자가 직접 사용자를 어떤 그룹에 속하도록 지정할 수도 있습니다. Community, Club 이라는 단위로 사용자들이 속할 수 있는 그룹을 만들었고, 이 그룹은 위에서 만들어진 Product를 구독(Subscription)하는 단위가 됩니다.

  • Community - 커뮤니티는 시스템에서 사용자가 속할 수 있는 가장 큰 단위 입니다.
  • Club - 클럽은 커뮤니티 내의 또다른 하위 그룹으로 커뮤니티에 속한 사용자가 가입할 수 있는 작은 그룹입니다.

제공하는 서비스를 바탕으로 Feature, Product를 구성하고 사용자 그룹까지 만들었으면 이들을 연결해 줄 수 있겠지요? 이 과정이 바로 서비스 구독(Subscription)이고 저희는 이 모든 것을 서비스 접근 제어 라고 하였습니다.

서비스 구독(Subscription)

그룹은 사용하고자 하는 상품을 선택하게 됩니다.  이러한 구독 정보를 등록하고 나서는 그때부터는 그룹에 속한 사용자들은 상품이 제공하는 서비스들을 자유롭게 이용할 수 있습니다.

서비스 접근제어 도메인 모델

위의 도메인 모델은 서비스 구성(Feature, Product)과 사용자 그룹(Community, Club) 그리고 구독(Subscription)의 관계를 보여주고 있습니다.

접근제어 UI

서비스 구독 정보를 설정하는 페이지 입니다. 사용자 그룹이 사용할 상품을 선택하여 구독 정보를 생성하게 됩니다.  그룹이 상품을 구독하도록 설정했다면 그룹에 속한 사용자는 서비스를 사용할 수 있게 되는 것입니다. 물론 이는 실시간으로 반영이 될 것입니다.

소스코드에서 서비스 허용여부를 체크하는 순서는 다음과 같습니다.

  1. 사용자의 서비스 요청 발생
  2. 컨트롤러로 진입하기 전 Interceptor에서 사용자가 요청한 서비스를 식별하는 값(URI와 HTTP메서드 등의 정보)과 사용자의 그룹 정보를 담아 접근제어 서버에 허용여부 요청
  3. 접근제어 서버에서는 사용자 그룹의 구독 리스트의 Product 목록을 확인, Product에 포함된 Feature 목록 확인, 그리고 Feature에 구성된 서비스를 확인하여 사용자가 요청한 서비스와 일치하면 허용
  4. Interceptor에서는 허용여부를 확인 후 통과, 혹은 접근 불가 예외 발생
  5. 사용자에게 접근이 허용된 경우 서비스의 결과를 응답으로 전송, 접근이 허용되지 않으면 해당하는 에러 메시지를 전송

하나 덧붙이자면, 구독 정보는 빠른 서비스를 위해 분산 캐시에 올라가고 접근 제어 서버에 동기화 됩니다. 지난번에 제가 포스팅 했던 [분산캐시 활용-Ehcache+Terracotta Server Array]에서 이러한 구독정보가 캐싱되고 있었습니다.

마무리

글을 쓰다보니 구현부분은 없고 지루한 설명이 길어졌습니다.^^ 사실 이전에도 비슷한 것들을 구현해본 경험은 많이 있었습니다. 웹 개발을 하면서 관리자 페이지에 흔히 요구되는 것들이 사용자, 메뉴, 역할, 권한관리 같은 것들이지요. 차이점이라면 이 프로젝트에서는 UI가 있는 웹 페이지가 아니라 JSON, XML, Binary 전문을 주고받는 웹 서비스 들이었습니다.

하지만 서비스 접근제어를 위해 설계한 Feature, Product와 같은 서비스들을 구성하는 개념, 사용자 그룹과 구독(Subscription)은 예전의 경험보다 좀더 유연하게 서비스 접근제어를 가져갈 수 있었습니다. 시스템의 다양한 사용자와 다양한 서비스를 자유롭게 구성하며 요구사항을 만족할 수 있었습니다.

스스로 가장 만족스러웠던 부분은 웹이 제공하는서비스 목록을 만들어내기 위해 일일히 수작업으로 등록하는 것이 아닌 빌드된 WAR를 직접 분석하여 자동화 했다는 점입니다. 변경이 되더라도 다시 버튼한번만 눌러주면 업데이트 해주니 정말 편했습니다... 좀더 다듬으면 다른 프로젝트에서도 유용하게 사용할 수 있을 듯 합니다.

여기까지 읽어 주신 분들에게 작은 부분이나마 도움이 되길 바라며 이만 글을 마무리 합니다.

감사합니다.

namoosori
안녕하세요. 나무소리 입니다. 나무소리는 넥스트리(주)의 교육 브랜드 입니다.넥스트리가 지난 20년 동안 쌓아온 개발 및 교육 경험들을 나무소리를 통해 많은 분들과 공유 하려고 합니다.앞으로 저희 나무소리를 통해 보다 나은 교육을 경험 하실 수 있도록 구성원 모두 최선을 다하겠습니다.