Silverlight RIA Services-jak najlepiej obsłużyć limit czasu sesji klienta?

Zbudowałem aplikację z Silverlight4, usług RIA i używam ASP.NET członkostwo w celu uwierzytelnienia/autoryzacji.

Moja sieć.config ma to:
<system.web>
 <sessionState timeout="20"/>
 <authentication mode="Forms">
  <forms name="_ASPXAUTH" timeout="20"/>
 </authentication>

Czytałem wiele różnych strategii, jak radzić sobie z auth / session timeout po stronie klienta. Oznacza to, że jeśli klient jest bezczynny przez X minut (20 tutaj), a następnie robi coś z interfejsem, który uruchamia wywołanie RIA / WCF, chcę uwięzić to zdarzenie i odpowiednio się z nim uporać (np. ekran logowania ) -- w skrócie: potrzebuję sposobu, aby odróżnić DomainException od domainexception po stronie serwera bona-fide vs. awaria auth, ponieważ czas sesji się skończył.

AFAIK: nie ma typowanego wyjątku ani właściwości, które mogłyby to określić. Jedynym sposobem, w jaki udało mi się to ustalić -- co wygląda jak hack: jest sprawdzenie ciągu komunikatu błędu i szukanie czegoś w rodzaju "Odmowa dostępu" lub "odmowa". Na przykład: coś takiego:

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

Więc to jest to, czym jestem obecnie działa i działa, jeśli uruchamiam i debuguję albo z wbudowanym serwerem z VS2010, albo jeśli uruchamiam w localhost IIS. Jeśli ustawiłem timeout na 1 minutę, Zaloguj się, poczekaj więcej niż minutę i uruchom kolejne połączenie, i breakpoint na wyjątku i wprowadzić blok kodu if powyżej i wszystko jest dobrze.

Następnie wdrażam aplikację na zdalny serwer IIS7 i próbuję tego samego testu i nie działa. Dodałem śledzenie logów i oto Zdarzenie, w którym wystąpił wyjątek:

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

Problem polega na tym, że że nie mam ciągu w komunikacie o błędzie, który wskazuje "denied" lub "Access denied" - i nie jestem pewien, dlaczego to rozwiązanie działa w localhost IIS lub VS2010 host, ale nie w zdalnym serwerze IIS7. Czy brakuje mi jakiegoś niejasnego ustawienia konfiguracji? Czy jest lepszy sposób, aby to zrobić w ogóle?

Author: zenocon, 2011-10-31

1 answers

Prawdopodobnie już to zrobiłeś, ale Ten artykuł opisuje korzystanie z DomainOperationException i sprawdzanie kodów błędów.

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

dla wygodnego dostępu (i w przypadku utraty dostępu do bloga) oto artykuł na blogu autorstwa Josha Eastburna:

Pytanie, które często pojawia się od programistów, którzy pracują z usługami Silverlight i WCF RIA: Dlaczego moja aplikacja Silverlight rzuca wyjątek, gdy jest bezczynna przez okres czasu? Jak można się spodziewać, wynika to z uwierzytelnionego czasu sesji. Ale to nie jest takie proste. Ponieważ Silverlight używa architektury klient / serwer, klient może działać niezależnie od serwera przez nieokreślony czas. Tylko wtedy, gdy klient Silverlight wykona połączenie z serwerem, zostanie zrealizowany limit czasu po stronie serwera. Istnieje kilka opcji, aby poradzić sobie z problemem limitu czasu klient-serwer (i być może uda ci się wymyślić kilka innych): jeśli nie przejmujesz się skutkami bezpieczeństwa związanymi z usunięciem limitu czasu sesji, możesz albo zwiększyć ustawienie limitu czasu w Internecie.config, lub utworzyć DispatcherTimer w kliencie Silverlight, który wywołuje prostą metodę na serwerze, aby działać jako " Keep Alive."Dodaj DispatcherTimer do klienta Silverlight, który pozostaje zsynchronizowany z timeoutem po stronie serwera i ostrzegaj / prompt użytkownika, aby aktywował sesję przed upływem czasu lub aby ponownie uwierzytelnił się, jeśli już wygasł. Jednakże, wymaga to dodatkowego wysiłku, aby synchronizować liczniki czasowe podczas wysyłania nowych żądań serwera. Pozwól serwerowi obsłużyć timeout tak, jak normalnie i z wdziękiem obsłużyć timeout na kliencie Silverlight. Oznacza to, że limit czasu jest określony przez aktywność wywołania serwera, a nie aktywność ograniczającą klienta Silverlight (tzn. dostęp do danych po stronie klienta w kontekście). Z tych trzech opcji uważam, że trzecia jest najlepszą równowagą bezpieczeństwa i użyteczności, a jednocześnie nie dodaje niepotrzebna złożoność aplikacji. Aby obsłużyć te limity czasu po stronie serwera globalnie, możesz dodać następującą logikę w metodzie Application_UnhandledException w aplikacji.xaml.cs lub w Twoim Global ViewModel loading construct jeśli go posiadasz:

 // 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; 
 }

Następujące stałe są zdefiniowane w klasie ErrorCodes:

public static class ErrorCodes 
{
     public const int NotAuthenticated = 0xA01;
     public const int Unauthorized = 401; 
}  

Gdy sesja po stronie serwera się skończy, wszelkie kolejne wywołania zwrócą wywołanie DomainOperationException. Sprawdzając zwrócony kod błędu, możesz określić, czy jest to błąd uwierzytelniania i odpowiednio go obsłużyć. W moim przykładzie wywołuję WebContext.Aktualne.Uwierzytelnianie.LoadUser (), która spróbuje ponownie uwierzytelnić użytkownika, jeśli to możliwe. Nawet jeśli użytkownik nie może być automatycznie ponownie uwierzytelniony, oddzwoni do metody My Application_UserLoaded. Tam mogę sprawdzić WebContext.Aktualne.Użytkownik.IsAuthenticated w celu ustalenia, czy kontynuować poprzednią operację, czy jeśli potrzebuję aby przekierować z powrotem do strony głównej i reprompt do logowania. Oto przykład kodu w wywołaniu zwrotnym Appliation_UserLoaded, który wyświetla okno dialogowe logowania, jeśli użytkownik nie jest uwierzytelniony:

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

Aby przetestować kod, możesz ustawić wartość timeout w web.config to a mała wartość, więc timeouty występują szybko:

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

Jeśli chcesz zobaczyć cały ten kod w działającym rozwiązaniu, sprawdź nasz szablon Silverlight Ria na CodePlex .

 10
Author: Aligned,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-09 17:36:09