이 섹션에서 설명하는 내용은 Spring Security를 사용해 도메인 객체의 보안을 관리하는 방법, 특히 Access Control Lists(ACLs)을 활용하는 방법입니다. 간단히 말해, 이 기능은 개별 객체에 대해 더 세부적인 권한 제어를 가능하게 합니다.
기본적인 애플리케이션 보안은 일반적으로 웹 요청(request)이나 메서드 호출(method invocation) 수준에서 이루어집니다. 예를 들어, 특정 사용자가 특정 URL에 접근할 수 있는지, 또는 특정 서비스 메서드를 호출할 수 있는지 확인하는 방식입니다. 하지만 복잡한 애플리케이션에서는 단순히 누가(Authentication) 요청을 보냈는지뿐만 아니라, 어떤 객체(SomeDomainObject)에 접근하려는지를 함께 고려해야 할 경우가 많습니다.
Spring Security를 사용하면, 위의 시나리오를 해결할 수 있는 몇 가지 구현 방법이 있습니다:
SecurityContextHolder.getContext().getAuthentication()를 사용해 현재 사용자의 인증 정보(Authentication 객체)를 가져와 확인합니다.AccessDecisionVoter를 사용해 권한을 확인 (GrantedAuthority 기반)Authentication 객체 내부에 저장된 GrantedAuthority[]를 기반으로 권한을 확인합니다.GrantedAuthority로 저장해둡니다.Authentication 객체를 생성하는 데 소요되는 메모리와 시간이 지나치게 커질 수 있습니다.AccessDecisionVoter에서 도메인 객체를 직접 접근위의 접근 방식들은 각각 장점과 단점이 있지만, 모두 어느 정도의 비효율성과 구현 부담을 동반합니다. 따라서, Spring Security는 자체적으로 ACL을 지원하여 이러한 문제를 해결하는 방법을 제공합니다.
Spring Security의 ACL은 다음과 같은 이점을 제공합니다:
Spring Security의 ACL에 대한 더 구체적인 설명은 다음 섹션에서 다뤄질 것입니다.
위 설명은 도메인 객체에 대해 권한을 부여하거나 제한할 때 Spring Security에서 선택할 수 있는 다양한 접근 방식을 비교한 것입니다. 각 방법은 상황에 따라 적합할 수 있지만, 일반적으로 효율성과 유지보수성을 고려하면 Spring Security의 기본 ACL 지원 기능을 사용하는 것이 더 나은 선택일 가능성이 높습니다.
Spring Security의 ACL 서비스는 spring-security-acl-xxx.jar에 포함되어 있습니다. 도메인 객체 인스턴스 보안 기능을 사용하려면 이 JAR 파일을 클래스패스에 추가해야 합니다.
Spring Security의 도메인 객체 인스턴스 보안 기능은 Access Control List(ACL) 개념에 기반합니다. 시스템 내 모든 도메인 객체 인스턴스는 고유한 ACL을 가지며, 해당 ACL에는 해당 도메인 객체를 사용할 수 있는 사용자와 사용할 수 없는 사용자에 대한 세부 정보가 기록됩니다. 이를 통해 Spring Security는 애플리케이션에 다음과 같은 주요 ACL 관련 기능을 제공합니다:
첫 번째 항목에서 언급했듯이, Spring Security ACL 모듈의 주요 기능 중 하나는 ACL을 고성능으로 검색하는 방법을 제공하는 것입니다. 이 ACL 저장소 기능은 매우 중요합니다. 왜냐하면 시스템 내 모든 도메인 객체 인스턴스가 여러 액세스 제어 항목(Access Control Entry)을 가질 수 있고, 각 ACL이 트리 구조로 다른 ACL을 상속받을 수 있기 때문입니다(이는 Spring Security에서 지원하며 매우 일반적으로 사용됩니다).
Spring Security의 ACL 기능은 다음과 같은 점을 고려하여 설계되었습니다:
ACL 모듈은 데이터베이스 중심으로 동작하며, 기본 구현에서 사용되는 주요 테이블 4개를 살펴보겠습니다. 일반적인 Spring Security ACL 배포에서 테이블 크기 순서로 나열됩니다(가장 많은 행을 가진 테이블이 마지막):
ACL_SID
ACL_CLASS
ACL_OBJECT_IDENTITY
ACL_ENTRY
ACL 시스템은 정수 비트 마스킹을 사용합니다. 하지만 비트 시프트(bit shifting)의 세부 사항을 몰라도 ACL 시스템을 사용할 수 있습니다.
Permission 인스턴스를 구현할 수 있습니다. 비트 마스킹은 도메인 객체 수와는 관계가 없습니다.
Acl
AccessControlEntry(ACE)
Permission
BasePermission 클래스에 정의되어 있습니다. Sid
PrincipalSid(Authentication 객체의 주체를 나타냄), GrantedAuthoritySid. ObjectIdentity
ObjectIdentityImpl. AclService
MutableAclService
ACL 시스템의 이해를 돕기 위해 이 샘플들을 참고하는 것을 권장합니다.
Spring Security의 ACL 기능을 시작하려면 ACL 정보를 저장할 위치가 필요합니다. 이를 위해 Spring에서 DataSource를 인스턴스화해야 합니다. 이 DataSource는 JdbcMutableAclService와 BasicLookupStrategy 인스턴스에 주입됩니다.
Spring Security와 함께 제공되는 샘플 중 하나를 참조하여 설정 예제를 확인하세요. 또한, 이전 섹션에서 언급된 ACL 관련 4개의 테이블을 데이터베이스에 추가해야 합니다(적절한 SQL 문은 ACL 샘플에서 확인 가능).
필요한 스키마를 생성하고 JdbcMutableAclService를 인스턴스화한 후, 도메인 모델이 Spring Security ACL 패키지와 상호 운용 가능하도록 설정해야 합니다.
ObjectIdentityImpl은 다양한 방식으로 사용할 수 있으므로 대부분의 경우 충분히 적합합니다.
public Serializable getId() 메서드가 포함되어 있다면, 추가적으로 ObjectIdentity에 대해 고민할 필요가 없을 수 있습니다. long 또는 int와 같은 long 호환 타입이라면 대부분 문제없이 동작합니다. long 식별자에 의존하므로, long을 사용하지 않을 경우 여러 클래스를 재구현해야 할 수 있습니다. long 타입은 모든 데이터베이스 시퀀스와 호환되고 가장 일반적인 식별자 데이터 타입이므로 Spring Security ACL 모듈에서 long 외의 식별자를 지원할 계획은 없습니다.아래 코드는 ACL을 생성하거나 기존 ACL을 수정하는 방법을 보여줍니다:
// 액세스 제어 항목(ACE)에 필요한 정보 준비
ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));
Sid sid = new PrincipalSid("Samantha");
Permission p = BasePermission.ADMINISTRATION;
// 관련 ACL 생성 또는 업데이트
MutableAcl acl = null;
try {
acl = (MutableAcl) aclService.readAclById(oi);
} catch (NotFoundException nfe) {
acl = aclService.createAcl(oi);
}
// ACE를 통해 권한 부여
acl.insertAce(acl.getEntries().length, p, sid, true);
aclService.updateAcl(acl);
위 예제에서는 식별자 44를 가진 Foo 도메인 객체와 연결된 ACL을 가져옵니다. 그런 다음, "Samantha"라는 이름의 주체가 해당 객체를 "관리"할 수 있도록 하는 ACE를 추가합니다.
insertAce 메서드 설명true(허용)를 사용하지만, false로 설정하면 해당 권한이 차단됩니다. Spring Security는 DAO나 레포지토리 작업의 일환으로 ACL을 자동으로 생성, 업데이트, 삭제하는 특별한 통합 기능을 제공하지 않습니다. 대신, 위 예제와 유사한 코드를 개별 도메인 객체에 대해 작성해야 합니다.
데이터베이스에 ACL 정보를 저장한 후, 이를 실제 권한 부여 결정 로직의 일부로 사용하는 단계가 필요합니다.
여기에는 여러 가지 방법이 있습니다:
1. AccessDecisionVoter 또는 AfterInvocationProvider를 작성
AclService를 사용해 관련 ACL을 검색한 후, Acl.isGranted(Permission[] permission, Sid[] sids, boolean administrativeMode)를 호출해 권한이 부여되었는지 또는 거부되었는지 판단합니다. AclEntryVoter AclEntryAfterInvocationProvider AclEntryAfterInvocationCollectionFilteringProvider이러한 클래스들을 사용하는 방법은 Spring Security의 샘플 애플리케이션을 참조하세요.