Silverlight RIA服务 - 如何最好地处理客户端身份验证会话超时?

时间:2011-10-30 23:33:15

标签: silverlight silverlight-4.0 asp.net-membership wcf-ria-services

我使用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[]&amp; outputs)
        at System.ServiceModel.DomainServices.Hosting.DomainOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]&amp; outputs)
        at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)
        at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)
        at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; 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服务器。我在这里缺少一些不起眼的配置设置吗?有没有更好的方法来做到这一点?

1 个答案:

答案 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