[英]OWIN ASP.NET - Avoid multiple logins for the same account without Identity in Web Api
我想知道如何阻止用戶同時使用多個刷新令牌。 讓我解釋:
問題是,如果另一個用戶使用相同的憑據登錄,則將為同一身份生成另一個刷新令牌。 所以,我想要做的是:如果某人使用具有活動刷新令牌的某些憑據再次登錄,而不是生成新的憑證,請替換現有的,或刪除它並插入新的。 因此,當訪問令牌到期時,先前的用戶將被斷開,因為刷新令牌不再存在。
另外,我如何實現一些服務來銷毀認證服務器中的刷新令牌? 因此,用戶可以調用它來斷開他的帳戶,而不僅僅是刪除cookie並等到它過期。
這是我的代碼:
Startup.cs:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}"
);
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/auth"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(1),
Provider = new OAuthProvider(),
RefreshTokenProvider = new RefreshTokenProvider()
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseWebApi(config);
}
}
OAuthProvider.cs:
public class OAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
try
{
var account = AccountRepository.Instance.GetByUsername(context.UserName);
if (account != null && Global.VerifyHash(context.Password, account.Password))
{
var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, account.Username));
claimsIdentity.AddClaim(new Claim("DriverId", account.DriverId.ToString()));
var newTicket = new AuthenticationTicket(claimsIdentity, null);
context.Validated(newTicket);
}
}
catch { }
return Task.FromResult<object>(null);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
}
RefreshTokenProvider.cs:
public class RefreshTokenProvider : AuthenticationTokenProvider
{
public override Task CreateAsync(AuthenticationTokenCreateContext context)
{
var refreshToken = new TokenModel()
{
Subject = context.Ticket.Identity.Name,
Token = GenerateToken(),
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(5)
};
context.Ticket.Properties.IssuedUtc = refreshToken.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = refreshToken.ExpiresUtc;
refreshToken.Ticket = context.SerializeTicket();
try
{
TokenRepository.Instance.Insert(refreshToken);
context.SetToken(refreshToken.Token);
}
catch { }
return Task.FromResult<object>(null);
}
public override Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
try
{
var refreshToken = TokenRepository.Instance.Get(context.Token);
if (refreshToken != null)
{
if (TokenRepository.Instance.Delete(refreshToken))
{
context.DeserializeTicket(refreshToken.Ticket);
}
}
}
catch { }
return Task.FromResult<object>(null);
}
private string GenerateToken()
{
HashAlgorithm hashAlgorithm = new SHA256CryptoServiceProvider();
byte[] byteValue = Encoding.UTF8.GetBytes(Guid.NewGuid().ToString("N"));
byte[] byteHash = hashAlgorithm.ComputeHash(byteValue);
return Convert.ToBase64String(byteHash);
}
}
還有一個問題:如何在catch中拋出內部服務器錯誤? 因為它實際上返回了invalid_grant
,但catch意味着數據庫錯誤,而不是無效的憑據或令牌。
謝謝你的幫助,對不好的英語感到抱歉。 我希望你明白!
考慮以下:
您不必阻止用戶。 訪問令牌很快就會過期,只需確保在刷新令牌時不發出新的訪問令牌。 您可以通過檢查刷新令牌來執行此操作。 如果加密的刷新令牌與數據庫'invalid_grant'中的加密刷新令牌不匹配,則將返回。 用戶只有一個選項:再次登錄。
如果用戶使用憑據登錄,則更新刷新令牌(也在數據庫中)。 這將自動使“舊”刷新令牌無效。
您可以在RefreshTokenProvider.CreateAsync中實現第2點和第3點。 一些偽代碼:
// using Microsoft.AspNet.Identity;
public override Task CreateAsync(AuthenticationTokenCreateContext context)
{
var form = context.Request.ReadFormAsync().Result;
var grantType = form.GetValues("grant_type");
if (grantType[0] != "refresh_token")
{
// your code
...
// One day
int expire = 24 * 60 * 60;
context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddSeconds(expire));
// Store the encrypted token in the database
var currentUser = context.Ticket.Identity.GetUserId();
TokenRepository.Instance.EncryptAndSaveTokenInDatabase(context.Token, currentUser);
}
base.Create(context);
}
關於錯誤,只需返回invalid_grant
。 您經常期望數據庫失敗? 客戶端將要求登錄或收到'invalid_grant'。 它知道如何處理(重定向到登錄頁面)。 客戶端不必知道存在數據庫錯誤。 如果您需要其他信息,可以在后端登錄。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.