web api - asp.net 身份令牌即使对于后续请求也会过期
Posted
技术标签:
【中文标题】web api - asp.net 身份令牌即使对于后续请求也会过期【英文标题】:web api - asp.net identity token expires even for the subsequent request 【发布时间】:2015-12-02 18:55:10 【问题描述】:我在 web api 中使用 asp.net 身份进行基于令牌的身份验证。
对于刷新令牌,我已经基于以下链接实现了
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/我添加了以下两个类,并在启动配置中提到。
从我通过 api 单独使用用户名和密码调用的用户界面
http://domain/token
当我调用上面的api时,请求直接转到方法ValidateClientAuthentication。
但在这个方法中,逻辑是,我们需要发送客户端 ID 和客户端密码。
在用户登录特定用户之前,我们如何知道这两个?
我认为工作流程应该是这样的,我们需要根据数据库检查用户名和密码,并且应该生成访问令牌和刷新令牌。
但是我在哪里做这个逻辑。
示例中提到的这个系统的工作流程是什么?
在这个系统之前,我会在我的应用程序中调用Common/login api,验证成功后,
我会调用代码让用户登录
var userIdentity=await user.GenerateUserIdentityAsync(UserManager);
AuthenticationManager.SignIn(new AuthenticationProperties() IsPersistent = isPersistent , userIdentity);
在上面的代码之后,我将根据用户身份生成访问令牌。
我已经多次尝试以下实现并且厌倦了流程。
请帮助我了解这里提到的逻辑和流程。
SimpleAuthorizationServerProvider
namespace AngularJSAuthentication.API.Providers
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
string clientId = string.Empty;
string clientSecret = string.Empty;
Client client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
context.TryGetFormCredentials(out clientId, out clientSecret);
if (context.ClientId == null)
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
//context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
using (AuthRepository _repo = new AuthRepository())
client = _repo.FindClient(context.ClientId);
if (client == null)
context.SetError("invalid_clientId", string.Format("Client '0' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
if (client.ApplicationType == Models.ApplicationTypes.NativeConfidential)
if (string.IsNullOrWhiteSpace(clientSecret))
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
else
if (client.Secret != Helper.GetHash(clientSecret))
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
if (!client.Active)
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] allowedOrigin );
using (AuthRepository _repo = new AuthRepository())
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
var props = new AuthenticationProperties(new Dictionary<string, string>
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
,
"userName", context.UserName
);
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
var currentClient = context.ClientId;
if (originalClient != currentClient)
context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
return Task.FromResult<object>(null);
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault();
if (newClaim != null)
newIdentity.RemoveClaim(newClaim);
newIdentity.AddClaim(new Claim("newClaim", "newValue"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
context.AdditionalResponseParameters.Add(property.Key, property.Value);
return Task.FromResult<object>(null);
SimpleRefreshTokenProvider
namespace AngularJSAuthentication.API.Providers
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
public async Task CreateAsync(AuthenticationTokenCreateContext context)
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
return;
var refreshTokenId = Guid.NewGuid().ToString("n");
using (AuthRepository _repo = new AuthRepository())
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
;
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
context.SetToken(refreshTokenId);
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] allowedOrigin );
string hashedTokenId = Helper.GetHash(context.Token);
using (AuthRepository _repo = new AuthRepository())
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
if (refreshToken != null )
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await _repo.RemoveRefreshToken(hashedTokenId);
public void Create(AuthenticationTokenCreateContext context)
throw new NotImplementedException();
public void Receive(AuthenticationTokenReceiveContext context)
throw new NotImplementedException();
【问题讨论】:
“如果我将令牌设置为 10 分钟过期,...,令牌在 10 分钟后过期。”嗯,好像有道理! ;-) 对于后续请求,令牌应该过期应该延长。令牌应在 10 分钟的空闲时间内过期.. 是的,我明白了。只是这句话对我来说是古玩。顺便说一句,检查我的答案和教程的链接,了解如何使用刷新令牌扩展会话。 您是否尝试过 Freerider 提到的刷新令牌? 而不是刷新令牌,有一个选项,例如为每个请求重置时间。你能给这个吗? 【参考方案1】:如何使用刷新令牌并将它们存储在数据库中,例如以下两个示例:
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/ http://leastprivilege.com/2013/11/15/adding-refresh-tokens-to-a-web-api-v2-authorization-server/正如第一个链接中广泛描述的那样,您可以创建自己的令牌提供程序实现来处理令牌刷新:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
public async Task CreateAsync(AuthenticationTokenCreateContext context)
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
return;
var refreshTokenId = Guid.NewGuid().ToString("n");
using (AuthRepository _repo = new AuthRepository())
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
;
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
context.SetToken(refreshTokenId);
【讨论】:
我已经添加了这些类并实现了。我不知道这些方法是否会被自动调用或者我需要调用这些方法。如果我需要手动调用,那么我不知道何时/何处调用。 在这个方法之前,我几乎不记得我们可以为后续请求重置时间。我们可以吗?这是正确的做法吗? @JeevaJsb 你能发布一个关于你如何实现你的类以及你如何使用它们的代码 sn-p 吗?从我的观点来看,上述方法(来自两个链接)似乎是有效的。 每次用户发出请求时都会自动执行吗?以上是关于web api - asp.net 身份令牌即使对于后续请求也会过期的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET MVC Web API 身份验证令牌安全问题
使用 asp.net web api 令牌在 mvc 网站上进行身份验证
ASP.net core web api:使用 Facebook/Google OAuth 访问令牌进行身份验证
Azure AD B2C 与 ASP.NET Web API 发出令牌,用于 Web API 中的身份验证和访问 MS Graph API