Silverlight RIA Services - 如何最好地处理客户端认证会话超时?

8

我使用Silverlight4、RIA Services创建了一个应用程序,并使用ASP.NET Membership进行认证/授权。

我的web.config文件中有以下内容:

<system.web>
 <sessionState timeout="20"/>
 <authentication mode="Forms">
  <forms name="_ASPXAUTH" timeout="20"/>
 </authentication>

我看过了许多关于如何处理客户端认证/会话超时的不同策略。即:如果客户端在x分钟(这里是20)内处于空闲状态,然后他们通过UI执行某些操作触发RIA/WCF调用,我希望能够捕获该事件并适当地处理它(例如将他们带回登录界面) - 简而言之:我需要一种区分真正的服务器端DomainException与授权失败因为会话超时的方法。
据我所知:没有类型化的异常或属性可以确定这一点。我能够确定这一点的唯一方法 - 似乎是一个hack:检查错误的消息字符串并查找类似“Access denied”或“denied”的内容。例如,像这样的内容:
if (ex.Message.Contains("denied"))
  // this is probably an auth failure b/c of a session timeout

所以,这是我目前正在做的事情。如果我使用VS2010内置服务器进行运行和调试,或者在本地主机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>

问题在于错误信息中没有指示“denied”或“Access denied”的字符串,我不确定为什么这种解决方法适用于本地IIS或VS2010主机,但不适用于远程IIS7服务器。我是否缺少某些不明确的配置设置?总的来说,有更好的方式来处理这个问题吗?

1个回答

10

您可能已经了解到,这篇文章描述了如何使用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客户端上优雅地处理超时。这意味着超时是由服务器调用活动而确定的,而不是由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中的超时值设置为一个较小的值,以便快速出现超时:
<authentication mode="Forms">   
     <forms name=".Falafel_ASPXAUTH" timeout="1" /> 
</authentication>   

如果您想在一个可工作的解决方案中看到所有这些代码,请查看我们在CodePlex上的Silverlight RIA 模板

谢谢 - 这正是我在寻找的。我本来想尝试博客上列出的第三个选项,但还没有时间去实现它。这个方法应该适合我。 - zenocon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接