Рубрики
Без рубрики

Использование веб-токенов JSON для авторизации

Статья, первоначально опубликованная на моем личном веб-сайте в разделе Использование JWT в Java Полезная функция веб-сайта… Помеченный как java, jwt, безопасность.

Статья, первоначально размещенная на моем личном сайте в разделе Использование JWT в Java

Полезной функцией веб-приложения является возможность разрешить клиенту доступ к определенным функциям приложения. Как только произойдет аутентификация, важно также проверить, имеет ли клиент доступ к запрошенной функции. Простой в использовании метод обеспечивается веб-токеном JSON. Они могут быть легко сгенерированы, могут содержать данные, необходимые для авторизации, и, самое главное, являются безопасными. Давайте посмотрим, как JWT генерируется сервером и затем проверяется.

Что такое JWT

Во-первых, давайте посмотрим на структуру JWT. На первый взгляд это может показаться не слишком интересным, но структура этой кажущейся случайной строки содержит всю необходимую информацию. Как следует из названия, он состоял из строки JSON, но в кодировке Base64. В первой части (выделенной красным цветом) хранится используемый алгоритм, во второй части – утверждения (необходимые для авторизации), а в последней части – подпись.

Эта подпись гарантирует, что поддельный JWT не может быть предоставлен и что существующий не может быть изменен для добавления новых утверждений или продления срока его службы. Это связано с тем, что токен подписан с использованием ключа, который известен только серверу. Я не буду вдаваться в подробности о структуре или о том, как рассчитывается и проверяется подпись (в Интернете достаточно материалов), но суть в том, что вы не можете их изменить и позже принять сервером.

Создание JWT

В зависимости от используемого вами языка программирования существует множество библиотек, которые выполняют большую часть необходимой работы. В этом случае я буду использовать библиотеку, предоставленную auth0 для Java. Он бесплатный, с открытым исходным кодом и прост в использовании.

Давайте предположим, что вы работаете на сервере, которому необходимо предоставить маркер доступа стороннему приложению. Администратор может предоставить маркер доступа и точно указать, к каким конечным точкам или функциям имеет доступ это приложение. Например, вы хотите разрешить приложению просмотра считывать данные только с конечной точки пользователя, в то время как другое, более продвинутое приложение, также может редактировать этих пользователей. Могут быть предоставлены два разных токена доступа (по одному для каждого клиента), которые содержат точные права доступа.

На стороне сервера все равно потребуется некоторая работа для проверки права доступа, хранящегося в токене, но легко сгенерировать столько токенов, сколько необходимо, и с динамически добавляемыми правами. Если вы хотите повысить права на приложение, просто предоставьте ему новый токен. Кроме того, JWTS также может иметь срок годности, если вы хотите предоставить доступ всего на несколько дней.

Итак, вот как мы это делаем. Сначала мы выбираем алгоритм, который хотим использовать для подписи. Для простоты я буду использовать HMAC256, который использует секретный пароль, однако вы можете использовать пару открытых и закрытых ключей с алгоритмом RSA, если хотите. Этот второй подход полезен, если клиенту также необходимо проверить JWT, чтобы убедиться, что он поступает с вашего сервера.

public class MyServer {
    public String generateJwt(String userId, List accessAreas) {
        Algorithm algorithm = Algorithm.HMAC256("mySecret");
        JWTCreator.Builder jwtBuilder = JWT.create()
                .withIssuer("myServer")
                .withClaim("accessGrantedBy", userId)
                .withClaim("accessArea", accessAreas);

        return jwtBuilder.sign(algorithm);
    }
}

Затем мы создаем конструктор для JWT (библиотека использует шаблон Builder) и добавляем эмитента (необязательно), который является сервером, предоставившим доступ, и утверждает, что это список областей доступа, которые мы разрешим использовать. Мы можем предоставить столько претензий, сколько потребуется. В конце мы подписываем JWT выбранным алгоритмом, и возвращается строковое значение.

Проверка JWT

Как только JWT был предоставлен клиенту, его можно использовать для авторизации в системе. Обычно это указывается в заголовке авторизации или, если применимо, хранится в виде файла cookie или в сеансе. Когда сервер получает JWT, он должен сначала проверить токен. Это также можно сделать с легкостью благодаря библиотеке auth0.

public DecodedJWT decodeAndValidateToken(String token) {
    try {
        Algorithm algorithm = Algorithm.HMAC256("mySecret");
        JWTVerifier verifier = JWT.require(algorithm)
                .withIssuer("myServer")
                .build();
        return verifier.verify(token);
    } catch (JWTVerificationException exception){
        return null;
    }
}

Как и раньше, мы инициализируем алгоритм, который будет использоваться. Важно, чтобы использовался один и тот же алгоритм и секрет, в противном случае проверка завершится неудачей. Далее мы создаем JWTVerifier и передаем алгоритм и (необязательно) эмитента. При вызове функции verify() для токена он будет декодирован. Если маркер недействителен, подпись неверна, срок его действия истек или он был изменен каким-либо образом, библиотека выдаст исключение.

Теперь, если JWT действителен, мы можем убедиться, что клиент действительно имеет доступ к запрошенной функции. Для этого мы можем проверить утверждения, хранящиеся в токене, и подтвердить его наличие.

public boolean hasAccess(DecodedJWT jwt, String areaToAccess) {
    if (jwt == null) return false;

    return jwt.getClaim("accessArea")
            .asList(String.class)
            .contains(areaToAccess);
}

Для простоты мы можем объединить проверку, декодирование и проверку доступа в одном методе, который можно вызывать при вызове конечной точки или функции. Ниже приведен полный класс, который делает это. Можно внести дополнительные усовершенствования, и в моей следующей статье на эту тему я покажу, как отменить JWT, чтобы у вас был полный поток входа/выхода.

public class MyServer {
    public String generateJwt(String userId, List accessAreas) {
        Algorithm algorithm = Algorithm.HMAC256("mySecret");
        JWTCreator.Builder jwtBuilder = JWT.create()
                .withIssuer("myServer")
                .withClaim("accessGrantedBy", userId)
                .withClaim("accessArea", accessAreas);

        return jwtBuilder.sign(algorithm);
    }

    public DecodedJWT decodeAndValidateToken(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256("mySecret");
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("myServer")
                    .build();
            return verifier.verify(token);
        } catch (JWTVerificationException exception){
            return null;
        }
    }

    public boolean hasAccess(DecodedJWT jwt, String areaToAccess) {
        if (jwt == null) return false;

        return jwt.getClaim("accessArea")
                .asList(String.class)
                .contains(areaToAccess);
    }

    public boolean hasAccess(String token, String areaToAccess) {
        DecodedJWT jwt = decodeAndValidateToken(token);
        return hasAccess(jwt, areaToAccess);
    }

Статья, первоначально размещенная на моем личном сайте в разделе Использование JWT в Java

Оригинал: “https://dev.to/pazvanti/using-json-web-tokens-for-authorization-4m6c”