Истечение срока действия JWT не работает при использовании даты истечения срока в UTC - PullRequest
4 голосов
/ 17 мая 2019

Я использую jjwt для создания токена jwt. Все отлично работает при установке даты истечения с местным системным временем, т.е.

Date expDate = new Date (new Date (). GetTime () + 180000); //java.util.Date

Но я попытался использовать дату в формате UTC и подписал токен jwt с той же 3-минутной датой истечения. И теперь он выбрасывает ExpiredJwtException, хотя даже я проверяю, как только создаю токен. Я использую SimpleDateFormat для установки часового пояса в UTC. Это мой код для создания токена с использованием jjwt в java:

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date expDate, issDate;
    try {
        expDate = (Date) simpleDateFormat.parse(sdf.format(new Date().getTime() + 180000));
        issDate = (Date) simpleDateFormat.parse(sdf.format(new Date().getTime()));
        JwtBuilder builder = Jwts.builder()
                .setExpiration(expDate)
                .setIssuedAt(issDate)
                .setId(id)
                .signWith(signingKey, signatureAlgorithm);
        jwtToken = builder.compact();
    } catch (ParseException ex) {
    }

Токен успешно создан. Я также могу проверить содержимое онлайн. expDate на 3 минуты опережает issDate. Я также вызываю метод для проверки токена сразу после его создания путем передачи созданного токена. Мой метод подтверждения имеет:

    try {
        Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token);
        log.info("jwt verification success");
    } catch (ExpiredJwtException exJwt) {
        log.info("expired jwt : \n{}", exJwt.getMessage());
    } catch (JwtException e) {
        log.info("tampered jwt");
    }

Но я получаю ExpiredJwtException. Ошибка

истек jwt: JWT истек в 2019-05-17T01: 24: 48Z. Текущее время: 2019-05-17T07: 06: 48Z, разница 20520836 миллисекунд. Позволил перекос часов: 0 миллисекунд.

Из моего журнала дата выдачи и срок действия в моем токене на данный момент:

issued date is: 2019-05-17T07:06:48.000+0545
expiry date is: 2019-05-17T07:09:48.000+0545

Как это происходит? И спасибо за помощь.

1 Ответ

6 голосов
/ 17 мая 2019

Здесь нет необходимости в SimpleDateFormat, поскольку Date представляет количество миллисекунд с эпохи Unix , то есть полуночи января1-е 1970 (UTC).

Что может вызвать путаницу , однако, это метод toString(), так как он применяет часовой пояс JVM по умолчанию при генерации строки, представляющей это значение.

Поскольку вы обеспокоены UTC, позвольте мне обратить ваше внимание на то, что на самом деле представляет собой Всемирное координированное время (UTC): это стандарт времени не часовой пояс) и это определяется высокоточными атомными часами в сочетании с вращением Земли.

Стандарт времени UTC корректировался несколько раз до 1972 года, когда были введены високосных секунд для поддержания UTC в соответствии с вращением Земли, которое не совсем ровно,и менее точный, чем атомные часы.Поскольку вращение Земли замедляется, время от времени нам приходится вставлять здесь и там дополнительные секунды:

A graph from xkcd documenting the war between timekeepers and time

При внутреннем значении Date предназначен для отражения UTC, но может не соответствовать этому точно из-за этих високосных секунд.

Java 8 и новый API для даты и времени

Даже если Date соответствует вашим потребностям, когда дело доходит до UTC, вам следует избегать этого.Это унаследованный класс сейчас.

Java 8 представила новый API для дат, времени, моментов и длительностей на основе календарной системы ISO.Ближайшим эквивалентом Date является Instant, который представляет временную метку, момент на временной шкале в UTC.

Чтобы зафиксировать текущий момент в UTC, вы можете использовать следующее:

Instant.now();              // Capture the current moment in UTC

И вы можете использовать следующее, чтобы получить строку, представляющую такое значение:

Instant.now().toString();   // 2019-05-17T12:50:40.474Z

Эта строка отформатирована в соответствии с ISO 8601 , где Z указывает, что данное время указано в UTC.

Взаимодействие с JJWT

Для взаимодействия с JJWT,который пока не поддерживает типы java.time, вы можете создать экземпляр Date из Instant:

Date.from(Instant.now());   // Convert from modern class to legacy class

А вот тест, который демонстрирует, как вы можете выдать и проверить токен:

@Test
public void shouldMatchIssuedAtAndExpiration() {

    Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

    Instant issuedAt = Instant.now().truncatedTo(ChronoUnit.SECONDS);
    Instant expiration = issuedAt.plus(3, ChronoUnit.MINUTES);

    log.info("Issued at: {}", issuedAt);
    log.info("Expires at: {}", expiration);

    String jws = Jwts.builder()
            .setIssuedAt(Date.from(issuedAt))
            .setExpiration(Date.from(expiration))
            .signWith(key)
            .compact();

    Claims claims = Jwts.parser()
            .setSigningKey(key)
            .parseClaimsJws(jws)
            .getBody();

    assertThat(claims.getIssuedAt().toInstant(), is(issuedAt));
    assertThat(claims.getExpiration().toInstant(), is(expiration));
}

Для приведенного выше примера я использовал JJWT 0.10.5 сзависимости, перечисленные в документации .В случае необходимости приведенный выше код был написан со следующими import инструкциями:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.security.Key;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.Date;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
...