diff options
author | 杨宇千 <crupest@outlook.com> | 2019-08-01 21:22:55 +0800 |
---|---|---|
committer | 杨宇千 <crupest@outlook.com> | 2019-08-01 21:22:55 +0800 |
commit | fded706989548a6f80aa7605ce70b7d20e49edb7 (patch) | |
tree | 234ec46171bb5d86d8ff569e69cdd41807df7ef7 | |
parent | c07297373df08bd605f5bec96020192bb6dec151 (diff) | |
download | timeline-fded706989548a6f80aa7605ce70b7d20e49edb7.tar.gz timeline-fded706989548a6f80aa7605ce70b7d20e49edb7.tar.bz2 timeline-fded706989548a6f80aa7605ce70b7d20e49edb7.zip |
Expired token now has a unique code.
-rw-r--r-- | Timeline.Tests/Helpers/TestUsers.cs | 6 | ||||
-rw-r--r-- | Timeline/Authenticate/AuthHandler.cs | 4 | ||||
-rw-r--r-- | Timeline/Controllers/TokenController.cs | 16 | ||||
-rw-r--r-- | Timeline/Services/JwtService.cs | 60 | ||||
-rw-r--r-- | Timeline/Services/UserService.cs | 3 |
5 files changed, 76 insertions, 13 deletions
diff --git a/Timeline.Tests/Helpers/TestUsers.cs b/Timeline.Tests/Helpers/TestUsers.cs index dd00e38d..ceecc7c0 100644 --- a/Timeline.Tests/Helpers/TestUsers.cs +++ b/Timeline.Tests/Helpers/TestUsers.cs @@ -17,13 +17,15 @@ namespace Timeline.Tests.Helpers { Name = "user", EncryptedPassword = passwordService.HashPassword("user"), - RoleString = "user" + RoleString = "user", + Version = 0, }); mockUsers.Add(new User { Name = "admin", EncryptedPassword = passwordService.HashPassword("admin"), - RoleString = "user,admin" + RoleString = "user,admin", + Version = 0, }); MockUsers = mockUsers; diff --git a/Timeline/Authenticate/AuthHandler.cs b/Timeline/Authenticate/AuthHandler.cs index 75d3b49f..41cb11c6 100644 --- a/Timeline/Authenticate/AuthHandler.cs +++ b/Timeline/Authenticate/AuthHandler.cs @@ -87,6 +87,10 @@ namespace Timeline.Authenticate return AuthenticateResult.Success(new AuthenticationTicket(principal, AuthConstants.Scheme)); } + catch (ArgumentException) + { + throw; // this exception usually means server error. + } catch (Exception e) { _logger.LogInformation(e, "A jwt token validation failed."); diff --git a/Timeline/Controllers/TokenController.cs b/Timeline/Controllers/TokenController.cs index 023bd53f..66c97b59 100644 --- a/Timeline/Controllers/TokenController.cs +++ b/Timeline/Controllers/TokenController.cs @@ -27,6 +27,7 @@ namespace Timeline.Controllers public const int Verify_BadToken = -2001; public const int Verify_UserNotExist = -2002; public const int Verify_BadVersion = -2003; + public const int Verify_Expired = -2004; } private readonly IUserService _userService; @@ -81,9 +82,18 @@ namespace Timeline.Controllers } catch (JwtTokenVerifyException e) { - var code = ErrorCodes.Verify_BadToken; - _logger.LogInformation(LoggingEventIds.VerifyFailed, e, "Attemp to verify a bad token because of bad format. Code: {} Token: {}.", code, request.Token); - return BadRequest(new CommonResponse(code, "A token of bad format.")); + if (e.ErrorCode == JwtTokenVerifyException.ErrorCodes.Expired) + { + var code = ErrorCodes.Verify_Expired; + _logger.LogInformation(LoggingEventIds.VerifyFailed, e, "Attemp to verify a expired token. Code: {} Token: {}.", code, request.Token); + return BadRequest(new CommonResponse(code, "A expired token.")); + } + else + { + var code = ErrorCodes.Verify_BadToken; + _logger.LogInformation(LoggingEventIds.VerifyFailed, e, "Attemp to verify a bad token because of bad format. Code: {} Token: {}.", code, request.Token); + return BadRequest(new CommonResponse(code, "A token of bad format.")); + } } catch (UserNotExistException e) { diff --git a/Timeline/Services/JwtService.cs b/Timeline/Services/JwtService.cs index e970bbd4..f3416cce 100644 --- a/Timeline/Services/JwtService.cs +++ b/Timeline/Services/JwtService.cs @@ -17,12 +17,52 @@ namespace Timeline.Services [Serializable] public class JwtTokenVerifyException : Exception { - public JwtTokenVerifyException() { } - public JwtTokenVerifyException(string message) : base(message) { } - public JwtTokenVerifyException(string message, Exception inner) : base(message, inner) { } + public static class ErrorCodes + { + // Codes in -1000 ~ -1999 usually means the user provides a token that is not created by this server. + + public const int Others = -1001; + public const int NoIdClaim = -1002; + public const int IdClaimBadFormat = -1003; + public const int NoVersionClaim = -1004; + public const int VersionClaimBadFormat = -1005; + + /// <summary> + /// Corresponds to <see cref="SecurityTokenExpiredException"/>. + /// </summary> + public const int Expired = -2001; + } + + public JwtTokenVerifyException(int code) : base(GetErrorMessage(code)) { ErrorCode = code; } + public JwtTokenVerifyException(string message, int code) : base(message) { ErrorCode = code; } + public JwtTokenVerifyException(Exception inner, int code) : base(GetErrorMessage(code), inner) { ErrorCode = code; } + public JwtTokenVerifyException(string message, Exception inner, int code) : base(message, inner) { ErrorCode = code; } protected JwtTokenVerifyException( System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } + + public int ErrorCode { get; private set; } + + private static string GetErrorMessage(int errorCode) + { + switch (errorCode) + { + case ErrorCodes.Others: + return "Uncommon error, see inner exception for more information."; + case ErrorCodes.NoIdClaim: + return "Id claim does not exist."; + case ErrorCodes.IdClaimBadFormat: + return "Id claim is not a number."; + case ErrorCodes.NoVersionClaim: + return "Version claim does not exist."; + case ErrorCodes.VersionClaimBadFormat: + return "Version claim is not a number"; + case ErrorCodes.Expired: + return "Token is expired."; + default: + return "Unknown error code."; + } + } } public interface IJwtService @@ -110,15 +150,15 @@ namespace Timeline.Services var idClaim = principal.FindFirstValue(ClaimTypes.NameIdentifier); if (idClaim == null) - throw new JwtTokenVerifyException("Id claim does not exist."); + throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.NoIdClaim); if (!long.TryParse(idClaim, out var id)) - throw new JwtTokenVerifyException("Can't convert id claim into a integer number."); + throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.IdClaimBadFormat); var versionClaim = principal.FindFirstValue(VersionClaimType); if (versionClaim == null) - throw new JwtTokenVerifyException("Version claim does not exist."); + throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.NoVersionClaim); if (!long.TryParse(versionClaim, out var version)) - throw new JwtTokenVerifyException("Can't convert version claim into a integer number."); + throw new JwtTokenVerifyException(JwtTokenVerifyException.ErrorCodes.VersionClaimBadFormat); return new TokenInfo { @@ -126,9 +166,13 @@ namespace Timeline.Services Version = version }; } + catch (SecurityTokenExpiredException e) + { + throw new JwtTokenVerifyException(e, JwtTokenVerifyException.ErrorCodes.Expired); + } catch (Exception e) { - throw new JwtTokenVerifyException("Validate token failed caused by a SecurityTokenException. See inner exception.", e); + throw new JwtTokenVerifyException(e, JwtTokenVerifyException.ErrorCodes.Others); } } } diff --git a/Timeline/Services/UserService.cs b/Timeline/Services/UserService.cs index c63ded1e..3164a645 100644 --- a/Timeline/Services/UserService.cs +++ b/Timeline/Services/UserService.cs @@ -208,6 +208,9 @@ namespace Timeline.Services public async Task<UserInfo> VerifyToken(string token) { + if (token == null) + throw new ArgumentNullException(nameof(token)); + TokenInfo tokenInfo; try { |