서비스 지향 솔루션에서 SSO를 효율적으로 사용
서비스 지향 솔루션은 구성 값을 저장하고 백엔드 시스템에 대한 자격 증명을 처리하기 위해 Enterprise SSO(Single Sign-On)를 사용합니다. 서비스 지향 솔루션은 지연 시간을 줄이기 위해 구성 값에 대해 로컬 캐시를 사용하며 5분마다 캐시를 새로 고칩니다.
많은 응용 프로그램에서 SSO 티켓 가져오기, 티켓 교환하기, 자격 증명을 사용하여 관련 응용 프로그램에 액세스하기 등의 SSO 작업은 어댑터가 처리합니다. 하지만 서비스 지향 솔루션의 인라인 버전은 어댑터를 사용하지 않습니다. 대신 코드에서 SSO를 사용해야 합니다.
이 항목에서는 서비스 지향 솔루션에서 사용되는 캐싱 메커니즘 및 솔루션의 인라인 버전이 코드에서 SSO를 사용하는 방법을 설명합니다.
구성 값의 로컬 캐싱
서비스 지향 솔루션은 ConfigPropertyBag 및 ConfigParameters의 두 개체를 사용하여 구성 값을 처리합니다. ConfigPropertyBag 클래스는 값을 보유하며 ConfigParameters 클래스에서만 사용됩니다. ConfigParameters 클래스는 솔루션의 다른 부분에서 구성 매개 변수를 검색하는 데 사용됩니다. 두 클래스 모두 Microsoft.Samples.BizTalk.WoodgroveBank.ConfigHelper 네임스페이스에 있습니다.
참고
서비스 지향 솔루션은 300초(5분) 간격으로 캐시 새로 고침 간격을 고정합니다. 대신 캐시 새로 고침 간격 자체를 이 솔루션에서 구성할 수 있는 속성으로 만들 수 있습니다. 이 과정은 비즈니스 프로세스 관리 솔루션에서 수행됩니다. 해당 솔루션이 SSO를 처리하는 방법에 대한 자세한 내용은 비즈니스 프로세스 관리 솔루션에서 SSO를 효율적으로 사용을 참조하세요. 그런 경우 변경된 새로 고침 간격은 기존 간격이 끝나 캐시를 새로 고친 후에 적용됩니다.
ConfigPropertyBag에는 다음과 같은 메서드가 있습니다.
메서드 | Description |
---|---|
읽기 | 지정된 속성의 값을 검색합니다. |
쓰기 | 속성에 값을 할당합니다. |
클래스는 .NET NameValueCollection의 instance 사용하여 값을 저장합니다. 두 액세스 메서드는 Microsoft.BizTalk.SSOClient.Interop 네임스페이스에서 IPropertyBag 인터페이스를 구현합니다. ConfigPropertyBag 클래스는 내부 클래스이며 ConfigParameters 클래스에서만 사용됩니다.
참고
속성 모음의 키 이름은 대/소문자를 구분하지 않습니다. 기본 NameValueCollection 은 대/소문자를 구분하지 않는 해시 및 대/소문자를 구분하지 않는 비교를 사용합니다.
애플리케이션은 ConfigParameters 클래스를 사용하여 SSO 구성 값을 처리합니다. 클래스에는 다음과 같은 공용 메서드 및 특성이 있습니다.
메서드 또는 특성 | Description |
---|---|
SSOConfigParameter | 구성 매개 변수를 지정하기 위한 열거입니다. |
GetConfigParameters | 지정된 매개 변수의 값을 검색하는 데 사용되는 메서드입니다. SSOConfigParameter를 사용하여 매개 변수를 나타냅니다. |
ConfigParameters 클래스는 .NET 타이머 클래스 및 대리자 함수를 사용하여 ConfigPropertyBag의 새로 고침을 설정합니다.
private static Timer cacheRefreshTimer;
private static ISSOConfigStore ssoConfigStore;
private static ReaderWriterLock syncLock;
// Cache refresh interval in milliseconds
private const int CacheRefreshInterval = 5 * 60 * 1000;
private static ConfigPropertyBag ssoPropBag;
static ConfigParameters()
{
ssoConfigStore = new ISSOConfigStore();
ssoPropBag = new ConfigPropertyBag();
syncLock = new ReaderWriterLock();
ssoConfigStore.GetConfigInfo(SSO_CONFIG_APP,
SSO_IDENTIFIER_NAME, SSOFlag.SSO_FLAG_RUNTIME,
ssoPropBag);
cacheRefreshTimer = new Timer(
new TimerCallback(ConfigParameters.cacheRefreshCallback),
null, CacheRefreshInterval, CacheRefreshInterval);
}
정적 생성자는 정적 구성원 변수를 초기화하여 클래스 인스턴스를 만들지 않고도 클래스 메서드를 사용할 수 있게 합니다. 생성자는 업데이트 및 읽기 중에 ConfigurationPropertyBag에 대한 액세스를 제어하는 데 사용되는 SSO 구성 저장소(ISSOConfigStore), 구성 속성 모음(ConfigPropertyBag) 및 동기화 잠금(ReaderWriterLock)의 인스턴스를 만듭니다. 그런 다음 생성자는 GetConfigInfo 를 사용하여 SSO 구성 값을 검색하고 속성 모음에 넣습니다. 마지막으로 생성자는 지정된 간격 후에 대리자 함수 cacheRefreshCallback을 호출하는 Timer 개체를 만듭니다.
타이머 대리자 함수는 비교적 간단합니다.
private static void cacheRefreshCallback(object state)
{
// Disable the timer until we are done loading the cache.
cacheRefreshTimer.Change(Timeout.Infinite, CacheRefreshInterval);
// Put the data from SSO in a new property bag so that
// we don't have to lock the property bag and block it from being
// used. The SSO call is a remote call and may take a while.
ConfigPropertyBag propBag2 = new ConfigPropertyBag();
ssoConfigStore.GetConfigInfo(SSO_CONFIG_APP,
SSO_IDENTIFIER_NAME, SSOFlag.SSO_FLAG_RUNTIME, propBag2);
// Get a writer lock before updating the cached values.
syncLock.AcquireWriterLock(Timeout.Infinite);
try
{
ssoPropBag = propBag2;
}
finally
{
syncLock.ReleaseWriterLock();
}
// Enable the timer.
cacheRefreshTimer.Change(CacheRefreshInterval,
CacheRefreshInterval);
}
캐시 새로 고침 콜백 메서드는 메서드가 끝까지 실행될 수 있도록 타이머를 비활성화합니다. 그리고 잠금을 사용하여 속성 모음에 대한 액세스를 제어합니다. ReaderWriterLock은 쓰기보다 읽기가 많은 경우를 위해 설계된 최상의 선택입니다. 또한 lock인 syncLock은 정적이며 모든 스레드가 동일한 단일 instance 공유하는 클래스 수준에서 선언됩니다.
코드에서 SSO 사용
코드에서 Single Sign-On을 사용하는 경우 코드는 어댑터 역할을 수행해야 합니다. 즉, 메시지에서 SSO 티켓을 검색하고, 티켓을 사용하여 백 엔드 시스템의 사용자 이름과 암호를 가져오고, 마지막으로 백 엔드 시스템을 사용해야 합니다. 서비스 지향 솔루션은 PendingTransactionsCaller 개체의 GetPendingTransactionsResponse 메서드를 통해 이 작업을 수행 합니다 .
메서드는 다음과 같습니다.
public static PendingTransactionsResponse GetPendingTransactionsResponse(XLANGMessage requestMsg)
{
try
{
// Get config parameter values.
int ptTimeout = Convert.ToInt32(
ConfigParameters.GetConfigParameter(
ConfigParameters.
SSOConfigParameter.
PENDING_TRANSACTIONS_INLINE_TIMEOUT
)
);
string ptURL = ConfigParameters.GetConfigParameter(
ConfigParameters.
SSOConfigParameter.
PENDING_TRANSACTIONS_URL
);
string ssoAffliateApp = ConfigParameters.
GetConfigParameter(ConfigParameters.
SSOConfigParameter.
PENDING_TRANSACTIONS_SSO_AFFILIATE_APP
);
// Redeem the SSO ticket and get the userid/password to
// use to interact with Pending Transaction System.
// Extract the ticket…
string msgTicket = (string)requestMsg.
GetPropertyValue(typeof(BTS.SSOTicket));
// and the user name of the originating user.
string originatorSID = (string)requestMsg.
GetPropertyValue(
typeof(
Microsoft.BizTalk.XLANGs.BTXEngine.OriginatorSID
)
);
string pendTransUserName;
// Now, redeem the ticket.
string[] pendTransCredential =
ssoTicket.RedeemTicket(
ssoAffliateApp,
originatorSID,
msgTicket,
SSOFlag.SSO_FLAG_NONE,
out pendTransUserName
);
PendingTransactionsRequest req =
(PendingTransactionsRequest)requestMsg[0].
RetrieveAs(
typeof(PendingTransactionsRequest)
);
PendingTransactionsResponse resp;
using (PendingTransactionsWebService
svc = new PendingTransactionsWebService())
{
svc.Url = ptURL;
svc.Timeout = ptTimeout;
// The web service uses basic authentication, so we
//need to send the user id and password in the request.
CredentialCache credCache = new CredentialCache();
NetworkCredential credentialToUse =
new NetworkCredential(
pendTransUserName, pendTransCredential[0]
);
credCache.Add(new Uri(svc.Url), "Basic", credentialToUse);
svc.Credentials = credCache;
resp = svc.GetPendingTransactions(req);
}
return resp;
}
catch (System.Net.WebException webEx)
{
if (webEx.Status == WebExceptionStatus.Timeout)
{
throw new PendingTransactionsTimeoutException();
}
else
{
Trace.WriteLine("Other Net.WebException: "
+ webEx.ToString()
+ (null == webEx.InnerException ? "" :
("Inner Exception: "
+ webEx.InnerException.ToString())
)
);
throw;
}
}
catch(System.Exception ex)
{
Trace.WriteLine("Other Exception: "
+ ex.ToString()
+ (null == ex.InnerException ? "" :
("Inner Exception: "
+ ex.InnerException.ToString())
)
);
throw;
}
}
메서드는 URL을 비롯한 백엔드 시스템의 구성 정보와 백엔드(관련) 응용 프로그램의 이름을 검색하는 것으로 시작됩니다.
티켓을 교환하려면 메서드는 메시지에서 티켓과 원래 요청한 사용자 이름을 추출해야 합니다. 메시지에는 티켓이 메시지 컨텍스트 속성인 BTS 중 하나로 포함 됩니다. SSOTicket. 자세한 내용은 UI 지침 및 개발자 API 네임스페이스 참조의 메시지 컨텍스트 속성을 참조하세요. 또한 메서드는 메시지 컨텍스트 속성에서 OriginatorSID 를 추출합니다. 메서드는 티켓 및 보낸 사람의 이름을 확보한 후 티켓에서 RedeemTicket 메서드를 호출하여 자격 증명을 검색합니다.
코드의 나머지 부분에서는 자격 증명에 대해 .NET NetworkCredential 캐시를 만들고 백엔드 웹 서비스를 호출합니다.
참고
사용자 이름과 암호는 SSO에서 일반 텍스트로 돌아오므로 이 정보를 포함하는 변수의 수명을 최소화하는 것이 좋습니다. 코드는 try 블록 내에서 자격 증명 변수를 선언합니다. 여기서 변수는 try 블록에서 종료될 때 만료됩니다.