2017-11-08 6 views
0

ASP.NET Core 2.0 웹 API에서 JWT 토큰 생성이 작동하지만 이전에 생성 된 토큰과 동일한 만료 시간을 갖는 문제가 발생합니다. 사람.새 JWT 토큰이 이전 토큰과 만료 됨

예를 들어 로그인 자격 증명을 게시하고 액세스 토큰을 반환합니다. 액세스 토큰은 [Authorize] API 끝점에서 예상대로 작동합니다. 테스트 목적으로 1 분 후에 만료 토큰을 설정합니다. 1 분 후 토큰이 만기되고 인증 된 엔드 포인트는 예상대로 401을 리턴합니다.

저는 클라이언트 측 응용 프로그램에서 401을 처리하고 있습니다. 로그인 양식이 나타나고 사용자가 다시 로그인합니다. 새 토큰이 생성되어 리턴됩니다. 유일한 문제는이 새로운 토큰이 초기에 생성 된 토큰과 정확히 동일한 'ValidTo'DateTime을가집니다.이 토큰이 이미 만료되었으므로이 새 토큰을 사용하여 401을 반환 한 후 모든 호출이 발생합니다. 두 개의 다른 토큰이 사용되고 있음을 확인했습니다.

Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler : 정보 : 유효성을 검사하는 데 실패는 문제가되지 않습니다 그래서 내가 (예상 만료 된 토큰) 잘못된 토큰

첫 번째 토큰 실패를 통과하여, 확인 토큰 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZGFtQHBytNDRiYS1 ... Do1NzM5NS8ifQ.t8DjvlGV7GZ3xucwu-1hlJRXA5owPdP9t7kfYiiJHyQ.

Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException : IDX10223 : 평생 유효성 검사가 실패했습니다. 토큰이 만료되었습니다.

ValidTo : '11// 2017 19시 23분 9초 08 '

현재 시간 : '11/08/2017 19시 23분 13초'. 정보 :

두 번째 토큰 실패

Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler를 (이전의 표시로 ValidTo 같은 예상하지) 토큰 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZGFtQ ... dDo1NzM5NS8ifQ을 확인하지 못했습니다. 2TMPJvYnQl1Jw78M2nj40uD3qejBEciXfKC845saGNI.

Microsoft.IdentityModel.Tokens.SecurityTokenExpiredException : IDX10223 : 평생 유효성 검사가 실패했습니다. 토큰이 만료되었습니다.

ValidTo : '11// 2017 19시 23분 9초 08 '

현재 시간 : '11/08/2017 19시 23분 34초'. Startup.cs에

JWT 구성 토큰 생성

services.Configure<JwtIssuerOptions>(options => { 
      options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)]; 
      options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)]; 
      options.SigningCredentials = new SigningCredentials(SigningKey, SecurityAlgorithms.HmacSha256); 
      options.ValidFor = TimeSpan.FromMinutes(1); 
     }); 
     services.AddAuthentication(o => 
     { 
      o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 
      o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 
     }).AddJwtBearer(o => 
     { 
      o.TokenValidationParameters = new TokenValidationParameters 
      { 
       ValidateIssuer = true, 
       ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)], 

       ValidateAudience = true, 
       ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)], 

       ValidateIssuerSigningKey = true, 
       IssuerSigningKey = SigningKey, 

       RequireExpirationTime = true, 
       ValidateLifetime = true, 
       ClockSkew = TimeSpan.Zero, 
      }; 
     }); 

로그인 작업 :

[HttpPost] 
    public async Task<IActionResult> Login([FromBody]CredentialsViewModel credentials) 
    { 
     if (!ModelState.IsValid) 
     { 
      return BadRequest(ModelState); 
     } 

     var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password); 
     if (identity == null) 
     { 
      return BadRequest(Errors.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState)); 
     } 

     // Serialize and return the response 
     var response = new 
     { 
      id = identity.Claims.Single(c => c.Type == "id").Value, 
      auth_token = await _jwtFactory.GenerateEncodedToken(credentials.UserName, identity), 
      expires_in = (int)_jwtOptions.ValidFor.TotalSeconds 
     }; 

     var json = JsonConvert.SerializeObject(response, _serializerSettings); 
     return new OkObjectResult(json); 
    } 
토큰이 생성되는

JwtFactory있어서

private readonly JwtIssuerOptions _jwtOptions; 

public JwtFactory(IOptions<JwtIssuerOptions> jwtOptions) 
{ 
    _jwtOptions = jwtOptions.Value; 
    ThrowIfInvalidOptions(_jwtOptions); 
} 

public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity) 
    { 
     var claims = new[] 
    { 
      new Claim(JwtRegisteredClaimNames.Sub, userName), 
      new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()), 
      new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64), 
      identity.FindFirst("rol"), 
      identity.FindFirst("id") 
     }; 

     // Create the JWT security token and encode it. 
     var jwt = new JwtSecurityToken(
      issuer: _jwtOptions.Issuer, 
      audience: _jwtOptions.Audience, 
      claims: claims, 
      notBefore: _jwtOptions.NotBefore, 
      expires: _jwtOptions.Expiration, 
      signingCredentials: _jwtOptions.SigningCredentials); 

     var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); 

     return encodedJwt; 
    } 

답변

0

문제 내 jwtFactory에 있었어, 나는 dependency injecti이었다. IOptions. 이것은 시작시에 정의되고 개체 작성시 자동으로 채워지는 여러 속성 (예 : DateTime.NowUtc를 가져 오는 IssuedAt)이 있으므로 IOptions는 처음로드 된 구성 만 반환합니다.

업데이트 된 IssuedAt 속성을 가진 새 버전의 JwtIssuerOptions를 잡는 IOptionsSnapshot을 삽입하여이 문제를 해결할 수있었습니다.

private readonly JwtIssuerOptions _jwtOptions; 

    public JwtFactory(IOptionsSnapshot<JwtIssuerOptions> jwtOptions) 
    { 
     _jwtOptions = jwtOptions.Value; 
     ThrowIfInvalidOptions(_jwtOptions); 
    } 
-1

그냥 제안의 _jwtOptions.Expiration는 토큰이 유효해야하는 시간, 즉, 시간 범위가 될, 그래서이 20이고 분,이 경우 비슷한 expires: DateTime.UtcNow.AddMinutes(_jwtOptions.Expiration) 또는 뭔가를해야 가정 해 봅시다해야한다. 그리고 그것을 반영하기 위해 이름을 바꿀 수도 있습니다.

+0

내 JwtIssuerOptions 모델에서 만료는 IssuedAt Datetime을 사용하고 ValidFor 시간 간격을 자동으로 추가하는 람다입니다. IssuedAt는 모델 선언에서 DateTime.UtcNow로 간단히 초기화됩니다. 제 경우에는 ValidFor 만 Startup에서 명시 적으로 설정해야합니다. JwtSecurityToken은 System.IdentityModel.Tokens.Jwt 내부의 클래스이며 만료하려면 DateTime이 필요합니다. Timespan이 아니고 – adam3039

+0

Timespan이 필요합니다. 그 이유는 그 설정 값에서'datetime'을 생성했기 때문입니다 –