【发布时间】:2016-09-19 02:12:33
【问题描述】:
场景
我正在使用 OWIN cookie 身份验证中间件来保护我的网站,如下所示
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
ExpireTimeSpan = new TimeSpan(0, 20, 0),
SlidingExpiration = true
});
}
登录时,我使用资源所有者密码流调用我的令牌服务并检索访问令牌和刷新令牌。
然后,我将刷新令牌、访问令牌和访问令牌过期时间添加到我的声明中,然后调用以下命令将此信息保存到我的身份验证 cookie 中。
HttpContext .GetOwinContext() 。验证 .SignIn(claimsIdentityWithTokenAndExpiresAtClaim);
然后在调用任何服务之前,我可以从当前声明中检索访问令牌并将其与服务调用相关联。
问题
在调用任何服务之前,我应该检查访问令牌是否已过期,如果已过期,请使用刷新令牌获取新令牌。一旦我有了新的访问令牌,我就可以调用该服务,但是我需要使用新的访问令牌、刷新令牌和到期时间来保存一个新的身份验证 cookie。
有什么好的方法可以对服务的调用者透明地做到这一点?
尝试的解决方案
1) 在调用每个服务之前检查
[Authorize]
public async Task<ActionResult> CallService(ClaimsIdentity claimsIdentity)
{
var accessToken = GetAccessToken();
var service = new Service(accessToken).DoSomething();
}
private string GetAccessToken(ClaimsIdentity claimsIdentity) {
if (claimsIdentity.HasAccessTokenExpired())
{
// call sts, get new tokens, create new identity with tokens
var newClaimsIdentity = ...
HttpContext
.GetOwinContext()
.Authentication
.SignIn(newClaimsIdentity);
return newClaimsIdentity;
} else {
return claimsIdentity.AccessToken();
}
}
这可行,但不可持续。此外,我不能再使用依赖注入来注入我的服务,因为服务在调用时需要访问令牌,而不是在构建时。
2) 使用某种服务工厂
在使用其访问令牌创建服务之前,它会在需要时执行刷新。问题在于我不确定如何让工厂返回服务并以一种很好的方式在实现中设置 cookie。
3) 改为在操作过滤器中执行。
我们的想法是会话 cookie 有 20 分钟的滑动到期时间。在每一个页面请求中,我都可以检查访问令牌是否超过其到期的一半(即,如果访问令牌的到期时间为一小时,请检查它是否有不到 30 分钟的到期时间)。如果是这样,请执行刷新。服务可以依赖未过期的访问令牌。假设您在 30 分钟到期之前点击了该页面并在该页面上停留了 30 分钟,假设会话超时(20 分钟空闲)将在您调用服务之前启动并且您将被注销。
4) 什么都不做,并在使用过期令牌调用服务时捕获异常
我想不出一个好方法来获取新令牌并再次重试服务调用,而不必担心副作用等。另外,最好先检查过期,而不是等待它的时间使服务失败。
这些解决方案都不是特别优雅。其他人如何处理这个问题?
【问题讨论】:
-
您对我的回答有任何疑问或问题吗?
-
我对 MVC 的解决方案更感兴趣。我很想看看自定义中间件的实现。但我的问题实际上是关于 a)您在哪个时间点检查访问令牌过期和 b)您如何透明地将访问令牌传递给需要它的服务(以便他们可以添加到他们的授权标头)。我还建议将访问令牌、刷新令牌和过期令牌存储在身份验证 cookie 中。我并不是建议将它们作为声明传递给服务。
-
请看我更新的答案。我花了一些时间研究什么是服务器端的最佳方法。请让我知道,如果你有任何问题。谢谢。