타 부서에서 현재 개발하고 있는 플랫폼에서 사용자 관리 부분을 따로 관리하고 싶다고 하였다.
그 관리는 SCIM 방식으로 하길 원했고, 나는 이에 따라 SCIM API Endpoint를 제공하기로 하였다....
(서비스 오픈을 앞두고 개인적으로 좀 진행하고 있던게 있었는데 그걸 멈추고 해야 했던지라 솔직히 하기 싫었다..ㅠㅠ....)
SCIM
클라우드 기반 애플리케이션 및 서비스에서 사용자 ID를 보다 쉽게 관리할 수 있도록 설계된 것
사용자 관리에 들어가는 비용과 복잡성을 줄이고자 하는 목적으로 개발되어 SCIM은 공통 사용자 스키마 및 확장 모델을 제공하고 REST API를 통해 이 스키마를 교환하기 위한 패턴을 제공하는 바인딩 문서를 제공합니다.
SCIM Schema Structure
SCIM 스키마는 많은 기존 배포 및 스키마에서 발견되는 공통 속성을 포함하는 사용자 및 그룹(리소스)을 나타내기 위한 최소 핵심 스키마를 제공합니다.
Resource은 하나 이상의 스키마로 식별되는 속성 모음입니다. 속성은 이름과 하나 이상의 단순 또는 복합 값으로 구성됩니다. SCIM 스키마는 속성의 데이터 유형, 복수 및 기타 구별되는 기능을 정의합니다. 달리 지정되지 않는 한 모든 속성은 소비자가 수정할 수 있습니다. 불변(읽기 전용) 속성은 속성 정의 내에서 'READ-ONLY'로 지정되어야 합니다(SHALL).
Model
SCIM 2.0은 리소스가 공통 분모이고 모든 SCIM 개체가 리소스에서 파생되는 개체 모델을 기반으로 합니다.
id, externalId, meta를 속성으로 갖고 RFC7643은 공통 속성을 확장한 User, Group, EnterpriseUser를 정의한다.
Schema Extenstion Model
SCIM 스키마는 LDAP에서 사용되는 ObjectClasses와 유사한 개체 확장 모델을 따릅니다. LDAP와 달리 상속 모델이 없습니다. 모든 확장은 추가됩니다. 스키마 확장은 공통 정의된 속성을 재정의해서는 안 되며 이 사양에 정의된 규칙을 따라야 합니다(SHOULD). 각 스키마 확장은 확장을 식별하는 데 사용되는 URI를 식별해야 합니다. XML은 XML 네임스페이스를 사용해야 하고 JSON 형식은 확장 리소스와 속성을 구별하기 위해 "스키마" 속성을 사용해야 합니다(MUST).
LDAP : 경량 디렉터리 액세스 프로토콜은 TCP/IP 위에서 디렉터리 서비스를 조회하고 수정하는 응용 프로토콜
SCIM Core Schema
각 SCIM 리소스(사용자, 그룹 등)에는 다음과 같은 공통 속성이 포함됩니다. 이러한 속성은 확장된 리소스 유형을 포함하여 모든 리소스에 포함되어야 합니다. 핵심 스키마가 암시적으로 포함되어 있으므로 이 문서에서 리소스가 완전히 정의된 경우 schema 속성을 지정할 필요가 없습니다.
Common Schema Attributes
id
서비스 공급자가 정의한 SCIM 리소스의 고유 식별자입니다. 리소스의 각 표현은 비어 있지 않은 id 값을 포함해야 합니다(MUST). 이 식별자는 서비스 제공자의 전체 리소스 집합에서 고유해야 합니다(MUST). id 속성의 값은 항상 서비스 제공자에 의해 발행되며 서비스 소비자에 의해 지정되어서는 안 됩니다(MUST).
externalId
서비스 소비자가 정의한 리소스의 식별자입니다. externalId는 소비자가 자체 식별자로 리소스를 참조할 수 있도록 하여 서비스 소비자와 서비스 제공자 간의 리소스 식별을 단순화할 수 있습니다. 공급자. 각 리소스는 비어 있지 않은 externalId 값을 포함할 수 있습니다(MAY). externalId 속성의 값은 항상 서비스 소비자로 발행되며 서비스 공급자가 지정할 수 없습니다.
meta
리소스 메타데이터를 포함하는 복합 속성입니다. 모든 하위 속성은 선택 사항입니다.
created
리소스가 서비스 공급자에 추가된 DateTime입니다. 속성은 DateTime이어야 합니다. 읽기 전용.
lastModified
이 리소스가 최초 생성 이후 수정된 적이 없는 경우 값은 생성된 값과 동일해야 합니다(MUST). 속성은 DateTime이어야 합니다. 읽기 전용.
location
반환되는 리소스의 URI입니다. 이 값은 Location HTTP 응답 헤더와 동일해야 합니다(MUST). 읽기 전용.
version
반환되는 리소스의 버전입니다. 이 값은 ETag HTTP 응답 헤더와 동일해야 합니다. 읽기 전용.
attributes
PATCH 작업 동안 리소스에서 제거할 속성의 이름입니다.
"schema" Attribute
SCIM은 확장 가능한 스키마와 함께 다양한 유형의 리소스를 지원합니다. 각 리소스는 정규화된 URL을 사용하여 표시되어야 합니다(MUST). 특정 표현에 스키마 표현에 대한 기존 지원이 있는 경우 해당 표현의 전통적인 규칙을 적용해야 합니다(MUST). 예를 들어 XML을 사용하여 사용자를 나타낼 때는 XML Namespace를 사용해야 합니다.
schemas
schemas 속성은 SCIM 표현에 대해 지원되는 스키마 버전과 해당 표현이 지원하는 모든 스키마 확장을 검사할 수 있는 문자열 배열입니다. 각 문자열 값은 고유한 URI여야 합니다. 이 사양은 사용자, 그룹 및 표준 "엔터프라이즈" 확장에 대한 URI를 정의합니다. SCIM 스키마의 모든 표현은 해당 표현에서 지원하는 URI 값과 함께 0이 아닌 값 배열을 포함해야 합니다(MUST). 중복 값은 포함되어서는 안 됩니다(MUST NOT). 값 순서가 지정되지 않았으며 동작에 영향을 주어서는 안 됩니다(MUST). 필수의.
SCIM User Schema
SCIM은 'urn:scim:schemas:core:1.0' URI를 사용하여 식별되는 사용자를 나타내는 스키마를 제공합니다. SCIM 핵심 스키마에 정의된 속성 외에 다음 속성이 정의됩니다.
SCIM은 'urn:scim:schemas:core:1.0' URI를 사용하여 식별되는 그룹을 나타내는 스키마를 제공합니다. 그룹 리소스는 명시적인 권한 부여 모델이 정의되어 있지 않더라도 공통 그룹 또는 역할 기반 액세스 제어 모델의 표현을 가능하게 하기 위한 것입니다.
SCIM 핵심 스키마에 정의된 공통 속성에 추가로 다음의 단일 속성이 정의됩니다.
displayName : 사람이 읽을 수 있는 그룹 이름입니다. (REQUIRED)
SCIM 핵심 스키마에 정의된 공통 속성 외에 다음 다중값 속성이 정의됩니다.
members : 그룹의 구성원 목록입니다. 표준 유형 "사용자" 및 "그룹"은 읽기 전용입니다. 값은 SCIM 리소스의 "id"(사용자 또는 그룹)여야 합니다. 그룹 유형의 의도는 서비스 공급자가 중첩된 그룹을 지원할 수 있도록 하는 것입니다.
Service Provider Configuration Schema
SCIM은 'urn: scim: schemas: core: 1.0' URI를 사용하여 식별된 서비스 제공자의 구성을 나타내는 스키마를 제공합니다.
서비스 공급자 구성 리소스를 사용하면 서비스 공급자가 SCIM 사양 준수를 표준화된 형식으로 노출할 수 있을 뿐만 아니라 추가 구현 세부 정보를 소비자에게 제공할 수 있습니다. 모든 속성은 읽기 전용입니다.
Core Schema에 정의된 공통 속성 외에 다음의 단일 속성이 정의됩니다.
Resource Schema
리소스 스키마는 리소스를 구성하는 속성과 메타데이터를 지정합니다. Resources 스키마는 읽기 전용이며 URI 'urn:scim:schemas:core:1.0'을 사용하여 식별됩니다. 다른 핵심 자원과 달리 Resources 스키마는 하위 속성 내에 복합 객체를 포함할 수 있으며 다른 속성이 지정되지 않는 한 모든 속성은 필수입니다.
Attributes로는 일단 name, description, schema, endpoint 등등이 있음
인증 정보 제공자(identity provider)와 서비스 제공자(service provider) 간의 인증 및 인가 데이터를 교환하기 위한 XML 기반의 개방형 표준 데이터 포맷이 SAML이다. SAML이 기술하는 가장 중요한 요구사항은 웹 브라우저통합 인증(SSO)이다.
Why SAML ?
SAML은 확장되는 수많은 플랫폼, 디지털 접점 및 장치에서 사용자를 인증하고 권한을 부여하는 안전한 방법을 제공합니다.
SAML은 IdP(사용자 자격 증명을 보유하는)와 SP 간에 IdP 방화벽 내부에서 발생하는 토큰 기반 인증 교환을 활성화하기 때문에 여러 웹 응용 프로그램 암호에 대한 필요성을 느끼지 못합니다.
SAML은 SSO(Single Sign-On) 기능을 활성화하여 주요 문제를 해결합니다. 즉, 사용자가 일정 기간 동안 단일 자격 증명 집합으로 여러 속성에 대해 등록 또는 로그인하고 권한을 부여할 수 있습니다. SAML은 또한 단일 로그아웃 기능을 활성화하여 사용자가 한 사이트에서 로그아웃할 때 해당 회사가 소유한 다른 모든 서비스 제공업체에서 자동으로 로그아웃되도록 합니다.
SAML 용어
Identity Provider(IDP) : 인증의 중앙 집중식 지점 - SSO내에서의 ID저장소
Service Provider(SP): 유저가 로그인을 시도하는 응용 프로그램
SAML Request : SP가 IDP에게 커뮤니케이션을 보내는 것
SAML Response : IDP가 SAML Request에 응답하는 것
Assertion : SAML Response에 담은 ID제공자가 사용자 인증을 포함하는 서비스 제공자에게 보내는 XML문서
Assertion은 인증/속성/권한 부여 세 가지 유형이 있다.
인증 Assertion은 사용자 식별을 증명하고, 로그인 한 시간과 인증 방법을 제공한다.
속성 Assertion은 SAML 속성을 서비스 공급자에게 전달합니다. SAML 속성은 사용자에 대한 정보를 제공하는 특정 데이터 조각입니다.
권한 부여 결정 Assertion은 사용자가 서비스를 사용할 수 있는 권한이 있는지 또는 IDP가 암호 오류 또는 서비스에 대한 권한 부족으로 인해 요청을 거부했는지 여부를 나타냅니다.
XML Signatures (DSig) : Assertion이 사용하는 XML 서명
Assertion Consumer Service : SP에서 Assertion을 받는 특정 EndPoint이며 XML Document를 검사한다.
Attribute : SAML Response에는 유저에 대한 여러 정보들
Relay State: SAML Response에서 매우 중요하게 알아야 되는 정보들
SAML 사용 예에서 주체는 서비스를 서비스 제공자로부터 요청한다. 이 서비스 제공자는 식별 어서션(assertion)을 인증 정보 제공자로부터 요청하여 가져온다. 이 어서션(assertion)에 기초하여, 서비스 제공자는 접근 제어 결정을 할 수 있다. 즉, 연결된 주체에 대해 일부 서비스를 수행할지의 여부를 결정할 수 있다.
위 용어를 토대로 SAML을 설명하자면
IdP(ID 공급자)가 SP(서비스 공급자)에 인증 자격 증명을 전달할 수 있도록 하는 open standard이다. SAML을 통해 하나의 자격 증명 세트를 사용하여 다양한 웹 사이트에 로그인할 수 있게 하며 ID 공급자와 서비스 공급자 간에 사용자 인증 및 권한 부여를 전달하는 안전한 방법이다.
How it works?
사용자가 Google에서 호스팅하는 서비스 또는 애플리케이션에 연결을 시도합니다.
서비스(응용 프로그램) 파트너는 파트너의 SSO 서비스에 대한 URL에 인코딩 및 포함된 SAML 인증 요청을 생성합니다. 식별자로 설정된 매개변수는 수정이나 검사 없이 다시 전달됩니다.
애플리케이션은 파트너의 SSO 서비스에 제출할 인코딩된 SAML 인증 요청을 포함하는 리디렉션을 사용자의 브라우저로 보냅니다.ID 파트너는 SAML 요청을 디코딩하고 사용자를 인증합니다.
ID 파트너는 인증된 사용자의 ID 및 속성이 포함된 SAML 어설션을 생성합니다.
인코딩된 SAML 응답은 브라우저로 다시 전달되고 브라우저는 응답을 ACS(액세스 제어 서버) URL로 보냅니다.
SAML 2.0 사양에 따라 이 응답은 파트너의 공개 및 비공개 DSA/RSA 키로 디지털 서명됩니다.
사용자가 Google 호스팅 애플리케이션에 로그인되어 있습니다.
사용자가 SAML 지원 애플리케이션에 로그인하면 서비스 제공자가 적절한 ID 제공자에게 권한 부여를 요청합니다. ID 제공자는 사용자의 자격 증명을 인증한 다음 사용자에 대한 권한을 서비스 제공자에게 반환하고 사용자는 이제 애플리케이션을 사용할 수 있습니다.
이 책에 나온 초보 개발자 나초보는 서비스를 개발하고 나서 보니 GC가 많이 발생된단 사실을 알게 되었고
성능을 올리기 위해 GC에 영향을 끼치고 있던 String 부분을 개선해보기로 하였다.
문제를 일으키던 부분은
String a = "test"; / for문을 통해 a += "XXX";
이런식으로 String에다 계속 값을 더하는 부분이었다.
String을 이렇게 사용하면 성능 적인 부분에서 문제를 일으킬 수 있다.
for문이 100,1000번 이상 돌아간다 가정해보면 메모리 사용률이 올라가고, 응답속도는 낮아질 것이다.
그렇다면 어떻게 해야 할까?
StringBuffer 클래스와 StringBuilder 클래스를 사용하여야 한다.
append() 메서드를 활용
StringBuffer 클래스와 StringBuilder 클래스의 차이는
StringBuffer 클래스는 스레드에 안전하게 설계되어 있으므로 여러 개의 스레드에서 하나의 StringBuffer객체를 처리해도 전혀 문제가 되지 않는다. 반면 StringBuilder 클래스는 단일 스레드에서의 안정성만을 보장한다.
String a = new String();
StringBuffer b = new StringBuffer();
StringBuilder c = new StringBuilder();
for(int loop = 0 ; loop < 10000; loop++) {
a += "abcde";
}
for(int loop = 0 ; loop < 10000; loop++) {
b.append("abcde");
}
String tempB = b.toString();
for(int loop = 0 ; loop < 10000; loop++) {
c.append("abcde");
}
String tempC = c.toString();
이런 소스가 실행된다면 결과가 어떻게 될까? 어느 것이 가장 빠르고 메모리를 적게 사용할까?
응답시간
생성된 임시 객체수
메모리 사용량
a (String)
95초
4,000,000
약 95Gb
tempB (StringBuffer)
0.24초
1400
약 37.5Mb
tempC (StringBuilder)
0.17초
1400
약 37.5Mb
그렇다, 응답시간과 메모리 사용량에서 엄청난 차이가 보인다
응답시간과 메모리 사용량에서 가장 좋은 성능을 보인건 StringBuilder이다.
왜 이럴까?
새로이 더해진 문자열은 새로운 주소를 갖는 객체가 생성이 되기 때문이다.
a에 "abcde"를 더하면 새로운 String클래스의 객체가 만들어지고, 이전에 있던 a객체는 필요 없는 쓰레기 값이 되어 GC대상이 된다. 이러한 작업이 반복 수행되면서 메모리를 많이 사용하게 되고, 응답 속도에도 많은 영향을 미치게 된다. GC를 하면 할수록 시스템의 CPU를 사용하게 되고 시간도 많이 소요된다. 그래서 프로그래밍을 할 때 메모리 사용을 최소화하는 것은 당연한 일이다.
Wrap-up
String은 짧은 문자열을 더할 경우 사용한다.
StringBuffer는 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템의 부분이 스레드에 안전한지 모를 경우 사용하면 좋다. (클래스에 static으로 선언한 문자열을 변경하거나, singleton으로 선언된 클래스에 선언된 문자)
StringBuilder는 스레드에 안전한지의 여부와 전혀 관계 없는 프로그램을 개발할 때 사용하면 좋다. 메서드 내에 변수를 선언하는 경우가 이에 해당되겠다.
WAS나 시스템이 JDK5.0 이상을 사용한다면, 컴파일러에서 자동으로 StringBuilder로 변환해 주긴 한다. 하지만 반복 루프를 사용해서 문자열을 더할 때는 객체를 계속 추가한다는 사실에 변함이 없으므로 StringBuilder, StringBuffer사용을 권장한다.