보안 시스템 API 프로그래밍

본 장에서는 보안 시스템 API 프로그래밍에 대해 설명한다.

1. 개요

사용자 애플리케이션에 자신만의 특별한 보안 기능을 추가하기 위해 보안 시스템 API를 사용하여 프로그래밍할 수 있다. 이러한 예로는 애플리케이션을 통해 등록한 사용자를 자동으로 JEUS 보안 시스템에 등록하는 Registration Servlet("auto-registration"이라고 한다)이 있다.

애플리케이션 프로그래머는 보안 서비스를 개발하기 전에 표준 Jakarta EE 보안 모델과 JEUS의 보안 서비스들이 원하는 보안 기능을 제공하는지 먼저 확인한다. 보안 API를 사용하여 프로그램을 개발하게 되면 Jakarta EE 서버 간의 호환성이 떨어진다. 일반적으로 Jakarta EE 서버 간의 호환성을 유지하기 위해 표준 Jakarta EE 보안 인터페이스만 사용하기를 권장한다.

2. Java SE Permission 설정

악의적인 사용자 코드(서블릿, EJB)로부터 JEUS 시스템을 보호하기 위해 보안 API를 사용할 수 있다.

사용자 코드에 보안 API를 사용하는 경우는 Java SE SecurityManager를 사용하거나 소스 코드(서블릿, EJB)가 SecurityCommonService.loginDefault(Subject)를 사용하여 성공적으로 로그인한 경우이다. 이때 Subject는 Target 보안 도메인의 accounts.xml에 미리 설정되어 있는 사용자에 대한 Subject로 policies.xml에 설정된 필요한 JEUS Permission을 가지고 있는 경우이다.

각 파일에 대한 설정 방법은 다음을 참고한다.

3. 기본 API

애플리케이션 프로그래밍 레벨에서 보안 시스템과 연동할 때 jeus.security.base 패키지의 클래스는 중요한 역할을 한다.

클래스 설명

jeus.security.base.Subject

사용자를 나타낸다.

Subject는 단 하나의 메인 Principal을 가지고 있으며, 메인 Principal이 Subject의 ID(username)로 취급된다. jeus.security.base.Subject 클래스에 여러 개의 String 속성값이 전달되기도 한다.

jeus.security.base.CredentialFactory

Subject 클래스의 멤버 변수이며, Subject에 대한 실제 Credential을 생성하는 데 사용된다.

예를 들어 PasswordFactory 클래스는 패스워드 Credential 인스턴스를 생성하고, JKSCertificateFactory 클래스는 JKS Keystore로부터 인증서를 얻어와서 인증서 Credential 인스턴스를 생성한다.

jeus.security.base.Policy

하나의 Principal-to-Role 매핑과 여러 개의 Role-to-Resource 매핑을 나타낸다. PermissionMaps을 멤버 변수로 가지고 있다.

jeus.security.base.PermissionMap

java.security.Permission 인스턴스들의 컨테이너로 볼 수 있으며, Policy 클래스의 멤버 변수이다.

jeus.security.base.Role

논리적 Role을 나타내는 인터페이스이다.

Role-to-Resource PermissionMap에서는 Role 인스턴스가 Resource Permission에 매핑된다. 마찬가지로 Principal-to-Role PermissionMap에서는 Principal이 Role Permission에 매핑된다.

jeus.security.base.SecurityCommonService

해당 Subject를 인증하고, Subject에 대한 Permission을 체크한다.

jeus.security.base.SecurityException

로그인 실패, 인증 실패, 권한 체크 실패 등의 보안을 위반했을 때 발생하는 예외이다.

jeus.security.base.ServiceException

보안 시스템에서 심각한 런타임 에러가 발생할 때 발생하는 예외이다.

4. 리소스 API

리소스와 관련해서 jeus.security.base 패키지의 몇 가지 기본 클래스뿐만 아니라 jeus.security.resource 패키지의 클래스도 중요한 역할을 한다.

클래스 설명

jeus.security.resource.PrincipalImpl

java.security.Principal 인터페이스의 구현 클래스이다.

jeus.security.resource.GroupPrincipalImpl

그룹 Principal을 나타내는 PrincipalImpl의 서브 클래스로 Nested한 그룹의 멤버를 관리하는 java.security.acl.Group 인터페이스의 구현 클래스이다.

jeus.security.resource.Password

단순한 Password Credential 인스턴스로 PasswordFactory에 의해 생성된다.

jeus.security.resource.PasswordFactory

Password Credential을 생성하는 CredentialFactory이다.

jeus.security.resource.Lock

일종의 Credential로 해당 Subject에 Lock을 건다.

Lock이 걸린 Subject로 로그인하게 되면 항상 실패하게 된다.

jeus.security.resource.LockFactory

Lock Credential을 생성하는 CredentialFactory이다.

jeus.security.resource.ExpiryTime

일종의Credential로 해당 Subject의 만료 기간을 설정한다.

Subject가 만료된 후 해당 Subjet를 사용하여 로그인을 시도하면 모두 실패한다.

jeus.security.resource.ExpiryTimeFactory

ExpiryTime을 생성하는 CredentialFactory이다.

jeus.security.resource.RoleImpl

Role 인터페이스를 구현한 클래스이다.

jeus.security.resource.RolePermission

특정 Principal이 특정 Role에 속한다는 것을 나타내는 java.security.Permission의 서브 클래스이다. RolePermission은 Principal-to-Role 매핑을 나타낼 때 사용된다.

jeus.security.resource.TimeConstrainedRolePermission

RolePermission의 서브 클래스로 현재 시간이 설정된 시간 범위 내에 있을 때만 Principal이 이 클래스의 슈퍼 클래스가 나타내는 Role에 속한다는 것을 나타낸다.

jeus.security.resource.ResourcePermission

java.security.Permission의 서브 클래스로 Role이 리소스에 접근해서 특정 액션을 실행할 수 있다는 개념을 나타낸다.

따라서 ResourcePermission은 Role-to-Resource 매핑을 나타낼 때 사용된다.

해당 클래스에 대한 자세한 정보는 Javadoc과 참고 자료를 확인한다.

5. SPI 클래스

보안 시스템의 근간을 이루는 서비스와 작업하려면, jeus.security.spi 패키지의 SPI 클래스를 사용해야 한다.

클래스 설명

jeus.security.spi.AuthenticationRepositoryService

Subject 저장소로부터 Subject를 추가, 삭제, 조회하는 데 사용된다. 이 API를 이용하면 Subject(user)를 프로그램 내에서 추가할 수 있다.

jeus.security.spi.AuthorizationRepositoryService

Policy 저장소로부터 Policy 데이터를 추가, 삭제, 조회하는 데 사용된다. 이 API를 이용하면 프로그램 내에서 Permission을 추가할 수 있다.

해당 클래스에 대한 자세한 정보는 Javadoc과 참고 자료를 확인한다. SPI 클래스에 대한 더욱 상세한 정보는 Custom 보안 서비스 개발을 참고한다.

6. 예제

다음 예제는 보안 API를 사용한 프로그램의 일부이다.

// Login the CodeSubject so that security checks are
// disabled (so that we can modify the Subject and Policy
// stores)
SecurityCommonService.loginCodeSubject();

// Make Subject with Principal “pete”
Principal petePrincipal = new PrincipalImpl(“pete”);
Subject pete = new Subject(petePrincipal);

// Make password “petepw” for Subject “pete”
PasswordFactory pf = new PasswordFactory(“petepw”);
pete.getCredentialFactories().add(pf);

// Add new Subject to the Subject store
AuthenticationRepositoryService.addSubject(pete);

// Make a new Policy
Policy policy = new Policy();

// Make role “someRole”
Role someRole = new RoleImpl(“someRole”);

// Make a RolePermission for role “someRole”
Permission rolePermission = new RolePermission(someRole);

// Add the RolePermission for “someRole” to the Policy
policy.getRolePolicy().addPermission(
     rolePermission, new Object[] {petePrincipal}, false, false);

// Create a ResourcePermission for resource “rsc1” with actions
// “action1” and “action2”
Permission rscPermission =
new ResourcePermission(“rsc1”, “action1,action2”);

// Add the ResourcePermission to the Policy using
// context id “ctx1”
policy.getResourcePolicy(“ctx1”, true).addPermission(
     rscPermission, new Object[] {someRole}, false, false);

// Add the new Policy to the Policy store
AuthorizationRepositoryService.addPolicy(policy);

// Logout the CodeSubject so that security checks are
// enabled again
SecurityCommonService.logout();

// Make a Subject to be logged in
Subject pete2 = Subject.makeSubject(“pete”, “petepw”);

// Login Subject “pete” (should succeed since we added
// “pete” earlier)
SecurityCommonService.loginDefault(pete2);

// Check ResourcePermission “rsc1” for current Subject (“pete”)
// Should succeed since we added Policy for this above
SecurityCommonService.checkPermission(
     “ctx1”, new ResourcePermissin(“rsc1”, “action2”);

// Print the name of the current Subject (“pete”)
System.out.println(
SecurityCommonService.getCurrentSubject().getPrincipal().getName());

// Logout “pete”
SecurityCommonService.logout();