我们有一个ASP.NET MVC 5 Web应用程序,我们使用AngularJS从MVC控制器(而不是ApiControllers)获取数据。其身份验证使用cookie身份验证链接到Azure AD,默认过期时间为1小时。
该应用程序是SPA。一旦用户登录,他们就不会导航到其他页面,而只使用ajax($ http)调用。
到目前为止,我们在Startup.Configuration()中扩展了RedirectToIdentityProvider方法,以识别ajax调用,并在令牌过期时将错误403返回给客户端。这样,我们避免重定向到权限页面并获得CORS错误。
此外,我们在同一类的TokenCache
中实现了持久性令牌缓存助手Microsoft.IdentityModel.Clients.ActiveDirectory
(名称空间AuthorizationCodeReceived
)。
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返回到客户端。我们应该在哪里以及如何做到这一点
答案 0 :(得分: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);
}
});
使用身份验证拦截器的另一种方法
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