Услуги Silverlight RIA - Как лучше всего обрабатывать время ожидания сеанса аутентификации клиента? - PullRequest
8 голосов
/ 31 октября 2011

Я создал приложение с Silverlight4, службами RIA и использую членство в ASP.NET для аутентификации / авторизации.

Мой web.config имеет следующее:

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

Я прочитал несколько различных стратегий о том, как бороться с тайм-аутом аутентификации / сеанса на стороне клиента.То есть: если клиент простаивает в течение x минут (20 здесь), а затем он что-то делает с пользовательским интерфейсом, который запускает вызов RIA / WCF, я хочу перехватить это событие и обработать его соответствующим образом (например, вернуть его обратно вэкран входа в систему) - в двух словах: мне нужен способ отличить добросовестную серверную сторону DomainException от ошибки аутентификации, поскольку время сеанса истекло.

AFAIK: нет типизированного исключения или свойства, котороемогу определить это.Единственный способ, которым я смог определить это - что похоже на взлом: проверить строку сообщения об ошибке и найти что-то вроде «Отказано в доступе» или «отказано».Например: что-то вроде этого:

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

Итак, это то, чем я сейчас занимаюсь, и оно работает, если я запускаю и отлаживаю либо со встроенного сервера VS2010, либо если я запускаю вlocalhost IIS.Если я установлю тайм-аут на 1 минуту, войду в систему, подожду больше минуты и вызову другой вызов, я остановлю точку останова на исключении и введу приведенный выше блок кода, и все хорошо.

Затем я разверну приложениеудаленный сервер 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>

Проблема в том, что у меня нет строки в сообщении об ошибке, которая указывает «отказано» или «Доступ запрещен»- и я не уверен, почему это решение работает на локальном хосте IIS или VS2010, а не на удаленном сервере IIS7.Есть какие-то неясные настройки конфигурации, которые мне здесь не хватает?Есть ли лучший способ сделать это в целом?

1 Ответ

10 голосов
/ 10 ноября 2011

Вы, вероятно, уже получили это, но эта статья описывает использование DomainOperationException и проверку кодов ошибок.

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

Для удобного доступа (и вв случае, если мы потеряли доступ к блогу) вот статья в блоге Джоша Истберна:

Вопрос, который часто задают разработчики, работающие с Silverlight и WCF RIA Services: почему мойПриложение Silverlight выдает исключение, когда оно простаивает в течение определенного периода времени?Как и следовало ожидать, это связано с тайм-аутом аутентифицированного сеанса.Но это не так просто.Поскольку Silverlight использует архитектуру клиент / сервер, клиент может работать независимо от сервера в течение неопределенного периода времени.Тайм-аут на стороне сервера реализуется только тогда, когда клиент Silverlight выполняет вызов на сервер.Существует несколько вариантов решения проблемы тайм-аута клиент-сервер (и, возможно, вам удастся придумать еще несколько): если вас не волнуют последствия для безопасности, связанные с удалением тайм-аута сеанса, вы можете либо увеличить тайм-аутнастройку в файле web.config или создайте DispatcherTimer в клиенте Silverlight, который вызывает простой метод на сервере, который действует как «Keep Alive».Добавьте DispatcherTimer к клиенту Silverlight, который синхронизируется с тайм-аутом на стороне сервера, и предупредите / попросите пользователя оставить сеанс активным до истечения времени или попросите его повторно пройти аутентификацию, если он уже истек.Однако это требует дополнительных усилий для синхронизации таймеров при выполнении новых запросов к серверу.Разрешите серверу обрабатывать время ожидания, как обычно, и корректно обрабатывать время ожидания на клиенте Silverlight.Это означает, что время ожидания определяется активностью вызова сервера, а НЕ активностью клиента Silverlight (т. Е. Доступа к данным на стороне клиента в контексте).Из этих трех вариантов я считаю, что третий является наилучшим балансом безопасности и удобства использования, и в то же время не добавляет ненужной сложности приложению.Чтобы обрабатывать эти тайм-ауты на стороне сервера глобально, вы можете добавить следующую логику либо в методе Application_UnhandledException в App.xaml.cs, либо в вашей глобальной загрузочной конструкции 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.Изучив возвращенный код ошибки, вы можете определить, является ли это ошибкой аутентификации, и обработать ее соответствующим образом.В моем примере я вызываю 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>   

Если вы хотите увидеть весь этот код в рабочем решении, ознакомьтесь с нашим SilverlightШаблон RIA на CodePlex .

...