Effiziente Verwendung des einmaligen Anmeldens in der dienstorientierten Lösung
Die dienstorientierte Lösung verwendet Einmaliges Anmelden für Unternehmen (SSO) sowohl zum Speichern von Konfigurationswerten als auch für den Umgang mit Anmeldeinformationen für die Back-End-Systeme. Um die Latenzzeit zu verringern, verwendet die Lösung einen lokalen Cache für die Konfigurationswerte. Der Cache wird alle fünf Minuten von der Lösung aktualisiert.
In vielen Anwendungen führen die Adapter die SSO-Vorgänge aus, einschließlich Abrufen eines SSO-Tickets, Einlösen des Tickets und Verwenden der Anmeldeinformationen, um auf die Partneranwendung zuzugreifen. Bei der Inlineversion der dienstorientierten Lösung werden jedoch keine Adapter verwendet. Hier muss SSO aus Code verwendet werden.
In diesem Thema wird der von der Lösung verwendete Zwischenspeichermechanismus beschrieben, und es wird erläutert, wie die Inlineversion der Lösung SSO aus Code verwendet.
Lokales Zwischenspeichern von Konfigurationswerten
Die dienstorientierte Lösung verwendet zwei Objekte, ConfigPropertyBag und ConfigParameters, um die Konfigurationswerte zu behandeln. Die ConfigPropertyBag-Klasse enthält die Werte und wird nur von der ConfigParameters-Klasse verwendet. Die ConfigParameters-Klasse wird von den anderen Teilen der Projektmappe verwendet, um die Konfigurationsparameter abzurufen. Beide Klassen befinden sich im Namespace Microsoft.Samples.BizTalk.WoodgroveBank.ConfigHelper .
Hinweis
Die dienstorientierte Lösung legt das Cacheaktualisierungsintervall auf 300 Sekunden (5 Minuten) fest. Sie können stattdessen das Cacheaktualisierungsintervall selbst zu einer konfigurierbaren Eigenschaft in dieser Lösung machen. Dazu wird die Lösung für die Geschäftsprozessverwaltung verwendet. Weitere Informationen dazu, wie diese Lösung einmaliges Anmelden verarbeitet, finden Sie unter Verwenden des effizienten einmaligen Anmeldens in der Geschäftsprozessverwaltungslösung. Beachten Sie, dass in diesem Fall eine Änderung des Cacheaktualisierungsintervalls erst in Kraft tritt, nachdem der Cache am Ende der alten Intervallzeit aktualisiert wurde.
ConfigPropertyBag verfügt über die folgenden Methoden:
Methode | BESCHREIBUNG |
---|---|
Lesen | Ruft einen Wert für eine bestimmte Eigenschaft ab. |
Schreiben | Weist einer Eigenschaft einen Wert zu. |
Die -Klasse verwendet eine instance einer .NET NameValueCollection, um die Werte zu enthalten. Die beiden Zugriffsmethoden implementieren die IPropertyBag-Schnittstelle aus dem Microsoft.BizTalk.SSOClient.Interop-Namespace . Die ConfigPropertyBag-Klasse ist eine interne Klasse und wird nur von der ConfigParameters-Klasse verwendet.
Hinweis
Bei Schlüsselnamen in der Eigenschaftensammlung wird die Groß- und Kleinschreibung nicht beachtet. Die zugrunde liegende NameValueCollection verwendet Hashing ohne Beachtung der Groß-/Kleinschreibung und Vergleiche ohne Beachtung der Groß-/Kleinschreibung.
Die Anwendung verwendet die ConfigParameters-Klasse , um die SSO-Konfigurationswerte zu verarbeiten. Die Klasse weist die folgenden öffentlichen Methoden und Attribute auf:
Methode oder Attribut | BESCHREIBUNG |
---|---|
SSOConfigParameter | Eine Enumeration zur Angabe von Konfigurationsparametern. |
GetConfigParameters | Eine Methode, mit der ein Wert für einen bestimmten Parameter abgerufen wird. Verwendet SSOConfigParameter zum Angeben des Parameters. |
Die ConfigParameters-Klasse verwendet die .NET Timer-Klasse und eine Delegatenfunktion, um die Aktualisierung von ConfigPropertyBag einzurichten:
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);
}
Der statische Konstruktor initialisiert die statischen Membervariablen und ermöglicht dadurch die Verwendung von Klassenmethoden, ohne eine Instanz der Klasse zu erstellen. Der Konstruktor erstellt Instanzen des SSO-Konfigurationsspeichers (ISSOConfigStore), des Konfigurationseigenschaftenbehälters (ConfigPropertyBag) und einer Synchronisierungssperre (ReaderWriterLock), mit der der Zugriff auf configurationPropertyBag während Updates und Lesevorgängen gesteuert wird. Der Konstruktor verwendet dann GetConfigInfo , um die SSO-Konfigurationswerte abzurufen und in den Eigenschaftenbehälter zu legen. Schließlich erstellt der Konstruktor ein Timer-Objekt , das nach dem angegebenen Intervall die Delegatenfunktion cacheRefreshCallback aufruft.
Die Funktion "Timer-Delegat " ist relativ einfach:
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);
}
Mit der Rückrufmethode für die Cacheaktualisierung wird der Timer deaktiviert, sodass die Methode uneingeschränkt ausgeführt werden kann. Beachten Sie die Verwendung der Sperren zum Steuern des Zugriff auf die Eigenschaftensammlung. ReaderWriterLock ist hier die beste Wahl – es ist für Fälle konzipiert, in denen mehr Lese- als Schreibvorgänge vorhanden sind. Beachten Sie auch, dass die Sperre syncLock statisch ist und auf Klassenebene deklariert wird, dass alle Threads denselben, einzigen instance gemeinsam haben.
Verwenden von SSO aus Code
Bei Verwendung des einmaligen Anmeldens von Code muss der Code die Rolle des Adapters übernehmen: Das heißt, das SSO-Ticket aus der Nachricht abrufen, das Ticket einlösen, um den Benutzernamen und das Kennwort für das Back-End-System abzurufen, und schließlich das Back-End-System verwenden. Die dienstorientierte Lösung erfolgt dazu über die GetPendingTransactionsResponse-Methode des PendingTransactionsCaller-Objekts .
Die Methode lautet wie folgt:
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;
}
}
Zunächst werden mit der Methode Konfigurationsinformationen, einschließlich der URL, für das Back-End-System sowie der Name der Back-End-Anwendung (Partneranwendung) abgerufen.
Zum Einlösen des Tickets muss die Methode das Ticket und den Benutzernamen der ursprünglichen Anforderung aus der Nachricht extrahieren. Die Nachricht enthält das Ticket als eine der Nachrichtenkontexteigenschaften BTS. SSOTicket. Weitere Informationen finden Sie unter Eigenschaften des Nachrichtenkontexts in der Referenz zu benutzeroberflächenbasierten API-Namespaces für Entwickler. Die Methode extrahiert auch die OriginatorSID aus den Nachrichtenkontexteigenschaften. Anhand des Tickets und des Absendernamens ruft die Methode die RedeemTicket-Methode im Ticket auf, um die Anmeldeinformationen abzurufen.
Mit dem restlichen Code wird ein .NET-Cache vom Typ NetworkCredential erstellt und der Back-End-Webdienst aufgerufen.
Hinweis
Da der Benutzername und das Kennwort aus SSO in Klartext zurückgegeben werden, können Sie die Lebensdauer von Variablen verkürzen, die diese Informationen enthalten. Beachten Sie, dass der Code die Anmeldeinformationsvariablen in einem Try-Block deklariert. Hier laufen die Variablen beim Beenden aus dem Try-Block ab.