Silverlight RIA服务-如何最好地处理客户端身份验证会话超时? [英] Silverlight RIA Services - How To Best Handle Client Auth Session Timeout?

查看:94
本文介绍了Silverlight RIA服务-如何最好地处理客户端身份验证会话超时?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Silverlight4,RIA Services构建了一个应用程序,并且正在使用ASP.NET Membership进行身份验证/授权.

I built an app with Silverlight4, RIA Services, and I'm using ASP.NET Membership for authentication/authorization.

我的web.config具有以下内容:

My web.config has this:

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

我已经阅读了许多有关如何在客户端上处理身份验证/会话超时的不同策略.也就是说:如果客户端闲置了x分钟(此处为20分钟),然后他们使用触发RIA/WCF调用的UI进行操作,则我想捕获该事件并进行适当处理(例如,将其返回到登录屏幕)-简而言之:由于会话超时,我需要一种与真正的服务器端DomainException与身份验证失败区分开的方法.

I have read a number of different strategies on how to deal with auth/session timeout on the client side. That is: if the client is idle for x minutes (20 here), and then they do something with the UI that triggers a RIA/WCF call, I want to trap on that event and deal with appropriately (e.g. take them back to the login screen) -- in a nutshell: I need a way to differentiate from a bona-fide server side DomainException vs. an auth failure because the session timed out.

AFAIK:没有类型的异常或属性可以确定这一点.我能够确定这一点的唯一方法-似乎是一种破解:是检查错误的消息字符串,并查找访问被拒绝"或被拒绝"之类的东西.例如:类似这样的东西:

AFAIK: there is no typed exception or property that can determine this. The only way I've been able to determine this -- which seems like a hack: is to inspect the Error's Message string and look for something like "Access denied" or "denied". For example: something like this:

if (ex.Message.Contains("denied"))
  // this is probably an auth failure b/c of a session timeout

因此,这就是我当前正在做的事情,并且如果我使用VS2010中的内置服务器运行和调试,或者如果我在localhost IIS中运行,则它可以工作.如果我将超时设置为1分钟,然后登录,等待一分钟以上并触发另一个调用,则我会在异常处断点并输入上面的if代码块,一切正常.

So, this is what I'm currently doing, and it works if I run and debug either with the built-in server from VS2010, or if I run in localhost IIS. If I set the timeout to 1 minute, login, wait more than a minute and trigger another call, I breakpoint on the exception and enter the if code block above and all is well.

然后,我将应用程序部署到远程IIS7服务器,然后尝试相同的测试,但它不起作用.因此,我添加了日志跟踪,以下是发生异常的事件:

Then I deploy the app to a remote IIS7 server and I try the same test and it doesn't work. So, I added log tracing, and here's the event where the exception happened:

<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服务器.我在这里缺少一些晦涩的配置设置吗?通常有更好的方法吗?

The problem is that I don't have the string in the error message that indicates "denied" or "Access denied" - and I am unsure as to why this solution works in localhost IIS or VS2010 host but not in a remote IIS7 server. Is there some obscure configuration setting that I'm missing here? Is there a better way to do this in general?

推荐答案

您可能现在已经了解了,但是本文介绍了如何使用DomainOperationException并检查错误代码.

You've probably gotten by this by now, but this article describes using the DomainOperationException and checking the error codes.

dex.ErrorCode == ErrorCodes.NotAuthenticated || dex.ErrorCode == ErrorCodes.Unauthorized

为方便访问(以防万一我们失去对博客的访问权限),以下是Josh Eastburn的博客文章:

使用Silverlight和WCF RIA Services的开发人员经常会提出一个问题:为什么我的Silverlight应用程序在闲置了一段时间后会引发异常?如您所料,这是由于经过身份验证的会话超时所致.但这不是那么简单.由于Silverlight使用客户端/服务器体系结构,因此客户端可以无限期地独立于服务器运行.只有当Silverlight客户端调用服务器时,服务器端超时才会实现.有一些选项可以处理客户端-服务器超时问题(并且您可能还可以提出其他一些解决方案):如果您不担心删除会话超时的安全隐患,则可以增加超时时间在web.config中进行设置,或在Silverlight客户端中创建DispatcherTimer,该客户端将在服务器上调用一个简单的方法来充当保持活动".将一个DispatcherTimer添加到Silverlight客户端,该客户端与服务器端超时保持同步,并警告/提示用户在该时间到期之前保持该会话为活动状态,或者如果该会话已过期,则让他们重新进行身份验证.但是,这需要付出额外的努力才能在发出新的服务器请求时使计时器保持同步.允许服务器像往常一样处理超时,并在Silverlight客户端上优雅地处理超时.这意味着超时是由服务器调用活动确定的,而不是由Silverlight客户端限制的活动(即,在上下文中访问客户端数据).在这三个选项中,我发现第三个选项是安全性和可用性之间的最佳平衡,同时又不会给应用程序增加不必要的复杂性.为了全局处理这些服务器端超时,可以在App.xaml.cs的Application_UnhandledException方法或全局ViewModel加载结构(如果有)中添加以下逻辑:

A question that comes up often from developers who are working with Silverlight and WCF RIA Services: why does my Silverlight application throw an exception when it has been idle for a period of time? As you might expect, it is due to the authenticated session timing out. But it isn’t quite that straightforward. Because Silverlight uses a client/server architecture, the client can operate independent of the server for an indefinite period of time. It is only when the Silverlight client makes a call to the server that the server-side timeout is realized. There are a few options to handle the client-server timeout issue (and you may be able to come up with a few more): If you aren’t concerned with the security implications of removing a session timeout, you can either increase the timeout setting in web.config, or create a DispatcherTimer in the Silverlight client that calls a simple method on the server to act as a "Keep Alive." Add a DispatcherTimer to the Silverlight client that stays in sync with the server-side timeout and warn/prompt the user keep the session active before the time expires or have them re-authenticate if it has already expired. However, this requires extra effort to keep the timers in sync when new server requests are made. Allow the server to handle the timeout as it normally would and handle the timeout gracefully on the Silverlight client. This means that the timeout is determined by server call activity, NOT activity confined the Silverlight client (i.e. accessing client-side data in the context). Of these three options, I find the third to be the best balance of security and usability while at the same time not adding unnecessary complexity to the application. In order to handle these server-side timeouts globally, you can add the following logic in either the Application_UnhandledException method in App.xaml.cs or in your global ViewModel loading construct if you have one:

 // 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类中定义了以下常量:

The following constants are defined within the ErrorCodes class:

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回调中一些代码的示例,如果用户未通过身份验证,则会显示一个登录对话框:

When the server-side session times out, any subsequent calls will return a DomainOperationException. By inspecting the returned ErrorCode, you can determine if it is an authentication error and handle it accordingly. In my example, I am calling WebContext.Current.Authentication.LoadUser() which will attempt to re-authenticate the user if possible. Even if the user can not be automatically re-authenticated, it will call back to my Application_UserLoaded method. There I can check WebContext.Current.User.IsAuthenticated to determine whether to proceed with the previous operation or if I need to redirect back to the home page and reprompt for login. Here is an example of some code in the Appliation_UserLoaded callback that shows a login dialog if the user is not authenticated:

// Determine if the user is authenticated
if (!WebContext.Current.User.IsAuthenticated) 
{
    // Show login dialog automatically
    LoginRegistrationWindow loginWindow = new LoginRegistrationWindow();
    loginWindow.Show(); 
}   

要测试您的代码,可以将web.config中的超时值设置为 较小的值,因此超时很快发生:

To test your code, you can set your timeout value in web.config to a small value so timeouts occur quickly:

<authentication mode="Forms">   
     <forms name=".Falafel_ASPXAUTH" timeout="1" /> 
</authentication>   

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

这篇关于Silverlight RIA服务-如何最好地处理客户端身份验证会话超时?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆