我使用Silverlight4,RIA Services构建了一个应用程序,并且我使用ASP.NET Membership进行身份验证/授权。
我的web.config有这个:
<system.web>
<sessionState timeout="20"/>
<authentication mode="Forms">
<forms name="_ASPXAUTH" timeout="20"/>
</authentication>
我已经阅读了许多关于如何在客户端处理身份验证/会话超时的策略。也就是说:如果客户端闲置x分钟(此处为20),然后他们使用触发RIA / WCF调用的UI执行某些操作,我想捕获该事件并进行适当处理(例如,将它们带回到登录屏幕) - 简而言之:我需要一种方法来区分真正的服务器端DomainException与auth失败,因为会话超时。
AFAIK:没有可以确定此问题的类型异常或属性。我能够确定这一点的唯一方法 - 这似乎是一个黑客攻击:是检查错误的消息字符串并查找“拒绝访问”或“拒绝”之类的内容。例如:类似这样的事情:
if (ex.Message.Contains("denied"))
// this is probably an auth failure b/c of a session timeout
所以,这就是我目前所做的,如果我使用VS2010的内置服务器运行和调试,或者我在localhost IIS中运行,它就可以工作。如果我将超时设置为1分钟,登录,等待超过一分钟并触发另一个呼叫,我在异常上断点并输入上面的if代码块,一切都很顺利。
然后我将应用程序部署到远程IIS7服务器,我尝试相同的测试,它不起作用。所以,我添加了日志跟踪,这是发生异常的事件:
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>131076</EventID>
<Type>3</Type>
<SubType Name="Error">0</SubType>
<Level>2</Level>
<TimeCreated SystemTime="2011-10-30T22:13:54.6425781Z" />
<Source Name="System.ServiceModel" />
<Correlation ActivityID="{20c26991-372f-430f-913b-1b72a261863d}" />
<Execution ProcessName="w3wp" ProcessID="4316" ThreadID="24" />
<Channel />
<Computer>TESTPROD-HOST</Computer>
</System>
<ApplicationData>
<TraceData>
<DataItem>
<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Error">
<TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.Diagnostics.TraceHandledException.aspx</TraceIdentifier>
<Description>Handling an exception.</Description>
<AppDomain>/LM/W3SVC/1/ROOT/sla-2-129644844652558594</AppDomain>
<Exception>
<ExceptionType>System.ServiceModel.FaultException`1[[System.ServiceModel.DomainServices.Hosting.DomainServiceFault, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message></Message>
<StackTrace>
at System.ServiceModel.DomainServices.Hosting.QueryOperationBehavior`1.QueryOperationInvoker.InvokeCore(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.DomainServices.Hosting.DomainOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
</StackTrace>
<ExceptionString>System.ServiceModel.FaultException`1[System.ServiceModel.DomainServices.Hosting.DomainServiceFault]: (Fault Detail is equal to System.ServiceModel.DomainServices.Hosting.DomainServiceFault).</ExceptionString>
</Exception>
</TraceRecord>
</DataItem>
</TraceData>
</ApplicationData>
</E2ETraceEvent>
问题是我在错误消息中没有指示“拒绝”或“拒绝访问”的字符串 - 我不确定为什么此解决方案在localhost IIS或VS2010主机中有效但在远程中不起作用IIS7服务器。我在这里缺少一些不起眼的配置设置吗?有没有更好的方法来做到这一点?
答案 0 :(得分:10)
到目前为止,你可能已经得到了这个,但this article描述了使用DomainOperationException并检查错误代码。
dex.ErrorCode == ErrorCodes.NotAuthenticated || dex.ErrorCode == ErrorCodes.Unauthorized
为方便访问(如果我们无法访问博客),这里是Josh Eastburn撰写的博客文章:
经常出现在使用Silverlight和WCF RIA服务的开发人员的问题:为什么我的Silverlight应用程序在空闲一段时间后会抛出异常?正如您所料,这是由于经过身份验证的会话超时。但这并不是那么简单。由于Silverlight使用客户端/服务器体系结构,因此客户端可以无限期地独立于服务器运行。只有当Silverlight客户端调用服务器时才会实现服务器端超时。有几个选项可以处理客户端 - 服务器超时问题(并且您可能能够提出更多):如果您不关心删除会话超时的安全隐患,您可以增加超时在web.config中设置,或者在Silverlight客户端中创建一个DispatcherTimer,它在服务器上调用一个简单的方法来充当“Keep Alive”。将DispatcherTimer添加到与服务器端超时保持同步的Silverlight客户端,并警告/提示用户在时间到期之前保持会话处于活动状态,或者如果已经过期则重新进行身份验证。但是,这需要额外的努力来在发出新的服务器请求时使计时器保持同步。允许服务器像往常一样处理超时,并在Silverlight客户端上正常处理超时。这意味着超时由服务器调用活动确定,NOT活动限制在Silverlight客户端(即访问上下文中的客户端数据)。在这三个选项中,我发现第三个选项是安全性和可用性的最佳平衡,同时不会给应用程序增加不必要的复杂性。为了全局处理这些服务器端超时,您可以在App.xaml.cs中的Application_UnhandledException方法或全局ViewModel加载构造中添加以下逻辑(如果有):
// Check for Server-Side Session Timeout Exception
var dex = e.ExceptionObject as DomainOperationException;
if ((dex != null) && (dex.ErrorCode == ErrorCodes.NotAuthenticated || dex.ErrorCode == ErrorCodes.Unauthorized) && WebContext.Current.User.IsAuthenticated)
{
// A server-side timeout has occurred. Call LoadUser which will automatically
// authenticate if "Remember Me" was checked, or prompt for the user to log on again
WebContext.Current.Authentication.LoadUser(Application_UserLoaded, null);
e.Handled = true;
}
以下常量在ErrorCodes类中定义:
public static class ErrorCodes
{
public const int NotAuthenticated = 0xA01;
public const int Unauthorized = 401;
}
当服务器端会话超时时,任何后续调用都将返回DomainOperationException。通过检查返回的ErrorCode,您可以确定它是否是身份验证错误并相应地处理它。在我的示例中,我正在调用WebContext.Current.Authentication.LoadUser(),如果可能,它将尝试重新验证用户。即使用户无法自动重新进行身份验证,它也会回调我的Application_UserLoaded方法。在那里,我可以检查WebContext.Current.User.IsAuthenticated以确定是否继续上一个操作,或者是否需要重定向回主页并重新登录以进行登录。下面是Appliation_UserLoaded回调中的一些代码示例,如果用户未经过身份验证,则会显示登录对话框:
// Determine if the user is authenticated
if (!WebContext.Current.User.IsAuthenticated)
{
// Show login dialog automatically
LoginRegistrationWindow loginWindow = new LoginRegistrationWindow();
loginWindow.Show();
}
要测试代码,可以将web.config中的超时值设置为a 小值,因此超时很快发生:
<authentication mode="Forms">
<forms name=".Falafel_ASPXAUTH" timeout="1" />
</authentication>
如果您希望在有效的解决方案中看到所有这些代码,请查看我们的Silverlight RIA Template on CodePlex。