2017년 12월 1일

스프링 시큐리티(Spring Security) - 도메인 객체 보안 (Domain Object Security ACL) 활용하기

대부분의  스프링 시큐리티를 사용하는 웹 응용프로그램들은  누가(Who)  어떤 URL 또는 어떤 메소드(Where)에 대한 호출에 대한 접근을 제안하는 방법으로 스프링 시큐리티를 사용하고 있다.


이런 접근 방식에서는 롤(ROLE)를 정의하고 사용자에게 롤(ROLE) 부여하고 특정 URL 또는 특정 클래스 함수에 대하여 ROLE 에 따른 접근제어를 하는 것으로 권한 관리를 구현하게 된다.

그러나 웹 프로그램들은 우리가 생각하는 이상으로 복잡하기 때문에 누가(Who) , 어디를 (Where) 뿐 아니라 무엇을 (What) 포함하여 권한을 설정할 수 있어야 한다.

예를 들어 아래와 게시판 프로그램을 디자인 한다고 가정해보자.

  • REQ1: Q&A게시판(B1)은 누구나(U1) 읽기(P1) / 쓰기(P2)가 가능하고 , 
  • REQ2: 자료실 게시판(B2)은 누구나(U1) 읽기(P1)는 가능하지만 관리자(U2)만 쓰기(P2)가 가능하고,  
  • REQ3: 고객지원 게시판(B3) 은 지정된 특정 사용자(U3) 만 읽기(P1) / 쓰기(P2)가 가능해야 한다.

누가(Who) , 어디를 (Where) 정보를 사용하여 권한을 결정하는 방식에서는 위의 기능을 구현하게 위하여 각기 다른 URL 또는 함수들 만들고 REQ3 요구사항을 위하여 추가로 권한 검사를 위하여 응용 프로그램 레벨에서 다시 권한결정을 위한  하드코딩이 필요하게 된다.

(Who) , 어디를 (Where) 뿐 아니라 무엇을 (What) 포함하여 권한을 결정하는 접근 방식에서는 게시판 객체 (What)에대한 권한을 설정할 수 있기 때문에 응용 프로그램 레벨에서의 하드 코딩 없이 구현이 가능하다. 이를 위하여 스프링 시큐리티는 도메인 객체 보안 (ACL) 서비스를 제공하고 있다.

스프링 시큐리티 ACL 사용하기 

도메인 객체 보안 (ACL) 서비스는   spring-security-acl-xxx.jar  라이브러리를 통하여 제공된다.  Spring 기반 웹 프로그램 개발 Part 2 - SpringSecurity 사용하기 와 같이 pom.xml 파일을 기술하였다면 관련 라이브러리는 포함되어 있다. (spring-security-taglibs 가 spring-security-acl 에 대한 의존성을 가지고 있기 때문에 자동으로 포함된다.)


    org.springframework.security
    spring-security-core
    ${project.dependency.spring-security.version}
    compile
  

    org.springframework.security
    spring-security-web
    ${project.dependency.spring-security.version}
    compile


    org.springframework.security
    spring-security-config
    ${project.dependency.spring-security.version}


    org.springframework.security
    spring-security-test
    ${project.dependency.spring-security.version}
    test  



또는 직접 다음과 같이 spring-security-acl 라이브러리를 추가해도 된다.


    org.springframework.security
    spring-security-core
    4.1.3.RELEASE
    compile
  

    org.springframework.security
    spring-security-web
    4.1.3.RELEASE
    compile


    org.springframework.security
    spring-security-config
    4.1.3.RELEASE


    org.springframework.security
    spring-security-acl
    4.1.3.RELEASE
    test  


다음으로 도메인 객체 보안 (ACL) 서비스를 사용하려면 ACL 정보를 어딘가에 저장해야 한다. 이를 위하여 ACL 데이터를 저장할 데이터베이스와 관련 테이블 생성이 필요하다. 테이블 생성은 spring-security-acl-xxx.jar 에 포함된 sql 파일을 이용하면 된다. ( mysql, oracle, postgres, SqlServer 에 대항하는 스크립트가 별도로 지원)



다음은 스크립트를 통하여 생성되는 테이블에 대한 다이어그램이다.



  • ACL_SID : 롤, 사용자에 대한 키 정보가 저장되는 테이블이다. ID 는 유니크한 숫자값, PRINCIPAL 는 롤의 경우는 0 사용자의 경우는 1 , SID 는 사용자 아이디 또는 롤 정보를 의미한다. 
  • ACL_CLASS : 도메인 객체 클래스에 대한 정보가 저장되는 테이블이다. ID 는 유니크한 숫자값, CLASS 는 클래스 이름을 의미한다. 
  • ACL_OBJECT_IDENTITY : 도메인 객체 인스턴스 정보가 저장되는 테이블이다. ID 는 유니크한 숫자값, OBJECT_ID_CLASS 는 클래스에 해당하는 ACL_CLASS.ID, OWNER_SID 는 생성자를 나타내는 ACL_SID.ID를 의미한다. 
  • ACL_ENTRY : 접근 권한 데이터가 저장되는 테이블이다. ID 는 유니크한 숫자값, OBJECT_ID_CLASS 는 클래스에 해당하는 ACL_CLASS.ID, OBJECT_ID_IDENTITY 는 객체 인스턴스를 나타내는 ACL_OBJECT_IDENTITY.ID , SID 는 권한이 부여된 대상을 의미하는 ACL_SID.ID, MASK 는 권한을 의미하는 마스크 값이다. 기본적으로 부여되는 모든 권한들은 유니크한 마스크 값을 갖는다.

예를 들어 dhson 사용자에게 ID 값이 9 인 architecture.community.board.Board 객체에 마스크 값이 1에 해당하는 READ 권한을 부여한다고 하면 다음과 같은 데이터들이 저장된다.

ACL_SID 
ID : 1, PRINCIPAL: 1, SID:dhson

ACL_CLASS
ID: 1, CLASS: architecture.community.board.Board

ACL_OBJECT_IDENTITY
ID: 1, OBJECT_ID_CLASS: 1, OBJECT_ID_IDENTITY: 9

ACL_ENTRY
ID: 1, ACL_OBJECT_IDENTITY: 1, SID: 1, MASH : 1

이제 아래와 같이 도메인 객체 보안 (ACL) 서비스 사용을 위하여 객체들을 **-context.xml 파일에 기술한다.

permissionSubsystemContext.xml


 Spring Security Domain Object ACL 설정

 
        
        
  
 
 
 
  
 
 
 
  
  
  
  
  
 
 
 
  
  
  
  
 
 
 
  
  
 
 
  
   
  
  
 
 
  
 
  
  
 
  
     
 



이제 자바 클래스에서 communityAclService 를 직접 사용하여 권한을 확인 하거나 @PreAuthorize 어노테이션을 사용하여 손쉽게 권한 제어가 가능하다.


  @PreAuthorize("hasPermission(#message, write) or hasPermission(#message, admin)")
  public void editMessage(Message message) { ... }


또는 아래와 같은 어노테이션도 가능하다.

  @PreAuthorize("hasPermission(#boardId, 'architecture.community.board.Board', 'READ')")
  @RequestMapping(value = {"/{boardId:[\\p{Digit}]+}/list"}, method = { RequestMethod.POST, RequestMethod.GET } )
  public String displayThreadList ( @PathVariable Long boardId,  HttpServletRequest request, HttpServletResponse response,  Model model) throws BoardNotFoundException{  
    / ***
  }


댓글 없음:

댓글 쓰기