2017-04-05 46 views
0

我們有一個ASP.NET MVC 5 web應用程序,我們使用AngularJS從MVC控制器(不是ApiControllers)獲取數據。它的身份驗證使用cookie身份驗證與默認的到期時間1小時後連接到Azure AD。當ajax調用失敗時在後端更新訪問令牌

該應用程序是SPA。一旦用戶登錄,他們不會導航到其他頁面,而只使用ajax($ http)調用。

到目前爲止,我們擴展了Startup.Configuration()中的RedirectToIdentityProvider方法,以識別ajax調用,並在令牌過期時將錯誤403返回給客戶端。這樣,我們避免重定向到權威頁面並獲得CORS錯誤。

此外,我們在同一類的AuthorizationCodeReceived中實現了持久令牌緩存助手TokenCache(命名空間Microsoft.IdentityModel.Clients.ActiveDirectory)。

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); 
app.UseCookieAuthentication(new CookieAuthenticationOptions()); 

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions 
    { 
     ClientId = ConfigurationHelper.ClientId, 
     Authority = ConfigurationHelper.AzureAdAuthorizationUri, 

     TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters 
     { 
      ValidateIssuer = true 
     }, 

     Notifications = new OpenIdConnectAuthenticationNotifications() 
     { 
      AuthorizationCodeReceived = (context) => 
      { 
       var code = context.Code; 

       ClientCredential credential = new ClientCredential(ConfigurationHelper.ClientId, ConfigurationHelper.AppKey); 
       String UserObjectId = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value; 

       AuthenticationContext authContext = new AuthenticationContext(ConfigurationHelper.AzureAdAuthorizationUri, new InMemoryTokenCache(UserObjectId)); 

       AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, ConfigurationHelper.AzureAdGraphResourceUri); 

       return Task.FromResult(0); 
      }, 


      RedirectToIdentityProvider = (context) => 
      { 
       if (IsAjaxRequest(context.Request)) 
       { 
        context.Response.StatusCode = 401; // for web API only! 
        context.Response.Headers.Remove("Set-Cookie"); 
        context.State = NotificationResultState.HandledResponse; 
       } 
       else 
       { 
        string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase; 

        context.ProtocolMessage.RedirectUri = appBaseUrl + "/" + context.Request.QueryString; 
        context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl; 
       } 

       return Task.FromResult(0); 
      }, 

      AuthenticationFailed = (context) => 
      { 
       // Suppress the exception 
       context.HandleResponse(); 

       return Task.FromResult(0); 
      } 
     } 
    }); 
} 
  • InMemoryTokenCache是我們的包裝器TokenCache

  • IsAjaxRequest是識別Ajax調用的函數。其餘的都是ASP.NET MVC 5模板的標準。

我們的問題是,當用戶訪問令牌到期時,我們要刷新它,並繼續使用,無需將用戶重定向到一個登錄屏幕或返回403客戶端去。我們在哪裏以及如何做到這一點?

回答

1

解決此問題的一種方法是在標記過期前刷新標記。 就我而言,我的應用程序由許多由Node.js服務器提供服務的單頁組成。 登錄後,我將token.expires_in值存儲在cookie中,可在服務器端訪問。

當用戶導航或點擊F5刷新頁面時,服務器用tokenExpiresIn初始化客戶端上下文。 如果令牌在100分鐘後過期,則會在90分鐘後自動刷新。

示例代碼

angular.module('app').run(function() { 

    var tokenExpiresIn = context['tokenExpiresIn']; 
    if (tokenExpiresIn) { 
     refreshToken(tokenExpiresIn); 
    } 

    // Automatically refresh token after a delay 
    function refreshToken(delay) { 
     $log.debug('Token will be refreshed in ' + delay + ' ms'); 
     $timeout(function() { 
     AuthenticationService.refreshToken().then(
     function (token) { 
      // Token refresh successful 
      // Broadcast event so that anyone can react if necessary 
      $rootScope.$broadcast(AuthenticationService.Events.REFRESH_TOKEN, token); 
      // Refresh token again after this one expires 
      refreshToken(token.expires_in * 1000 * (90/100); 
     }, function (error) { 
      // Token is invalid, force logout 
      AuthenticationService.logout(); 
     }); 
     }, delay); 
    } 

}); 

另一個way是爲使用身份驗證攔截

angular 
    .module('app') 
    .factory('authenticationHTTP401Interceptor', authenticationHTTP401Interceptor) 

    // Intercept 401 Unauthorized http response from Backend 
    authenticationHTTP401Interceptor.$inject = ['$q']; 
    function moAuthenticationHTTP401Interceptor($q) { 
     return { 
      responseError: function(rejection) { 
       if (rejection.status === 401 
       && rejection.config.url 
       && rejection.config.url.indexOf(context.BACKEND_BASE_URL') === 0 
       && rejection.headers("WWW-Authenticate") 
       && rejection.headers("WWW-Authenticate").indexOf('error="invalid_token"') !== -1 
       && rejection.headers("WWW-Authenticate").indexOf('error_description="The access token expired"') !== -1 
      ) 
       // Or using a RegExp 
       // if (rejection.status === 401 
       // && /invalid_token.*The access token expired/.test(rejection.headers("WWW-Authenticate")) 
       //) 
       { 
       // Refresh token here 
       // Display an overlay while doing it if necessary 
       } 
       return $q.reject(rejection); 
      } 
     }; 
    } 

來源:RFC 6750

+0

這是一個選項,但該理論認爲我不應該這樣做。刷新令牌僅用於獲取訪問令牌,並且僅在用戶稍後過期後請求某些內容。 –

+0

我已經添加了另一個解決方案,希望它有幫助;) –

相關問題