2017-04-05 49 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

我已经添加了另一个解决方案,希望它有帮助;) –

相关问题