面向服务的解决方案使用企业单一 Sign-On(SSO)来存储配置值和处理后端系统的凭据。 为了降低延迟,解决方案对配置值使用本地缓存。 解决方案每隔五分钟刷新一次缓存。
在许多应用程序中,适配器处理 SSO作,包括获取 SSO 票证、兑换票证,以及使用凭据获取对关联应用程序的访问权限。 但是,面向服务的内联版本解决方案不使用适配器。 它必须通过代码使用 SSO。
本主题介绍解决方案使用的缓存机制,以及解决方案的内联版本如何使用代码中的 SSO。
配置值的本地缓存
面向服务的解决方案使用两个对象 ConfigPropertyBag 和 ConfigParameters 来处理配置值。 ConfigPropertyBag 类保存值,并且仅由 ConfigParameters 类使用。 解决方案的其他部分使用 ConfigParameters 类来检索配置参数。 这两个类都在 Microsoft.Samples.BizTalk.WoodgroveBank.ConfigHelper 命名空间中。
注释
面向服务的解决方案以 300 秒(5 分钟)修复缓存刷新间隔。 相反,你可能希望使缓存刷新间隔本身成为此解决方案中的可配置属性。 这在业务流程管理解决方案中完成。 有关该解决方案如何处理 SSO 的详细信息,请参阅 业务流程管理解决方案中的“有效使用 SSO”。 请注意,在这种情况下,在旧时间间隔结束时刷新缓存之前,刷新间隔中的更改不会生效。
ConfigPropertyBag 具有以下方法:
方法 | DESCRIPTION |
---|---|
读取 | 检索给定属性的值。 |
写入 | 向属性分配值。 |
该类使用 .NET NameValueCollection 的实例来保存值。 这两种访问方法从 Microsoft.BizTalk.SSOClient.Interop 命名空间实现 IPropertyBag 接口。 ConfigPropertyBag 类是一个内部类,仅供 ConfigParameters 类使用。
注释
属性包中的键名称不区分大小写。 基础NameValueCollection使用不区分大小写的哈希和比较。
应用程序使用 ConfigParameters 类来处理 SSO 配置值。 该类具有以下公共方法和属性:
方法或属性 | DESCRIPTION |
---|---|
SSOConfigParameter | 用于指定配置参数的枚举。 |
获取配置参数 | 用于检索给定参数的值的方法。 使用 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);
}
请注意,静态构造函数初始化静态成员变量,从而启用类方法的使用,而无需创建类的实例。 构造函数创建 SSO 配置存储(ISSOConfigStore)、配置属性包(ConfigPropertyBag)和同步锁(ReaderWriterLock)的实例,用于在更新和读取期间控制对 ConfigurationPropertyBag 的访问。 然后,构造函数使用 GetConfigInfo 检索 SSO 配置值,并将其放入属性包中。 最后,构造函数创建一个 Timer 对象,该对象在指定的间隔后调用委托函数 cacheRefreshCallback。
计时器委托函数相对简单:
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 在这里是最佳选择,它专为读取量多于写入的情况而设计。 另请注意,锁、 syncLock 是静态的,在类级别声明所有线程共享同一个单一实例。
在代码中使用 SSO
在代码中使用单一登录时,代码必须承担适配器的角色:即,从消息中检索 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 缓存,并调用后端 Web 服务。
注释
由于用户名和密码以明文形式从 SSO 中返回,因此你想要最大程度地减少保存该信息的变量的生存期。 请注意,代码在 try 块中声明凭据变量。 在这里,变量在退出 try 块时过期。