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

Самодельная аутентификация JWT для Javalin

Аутентификация с помощью токенов стала обязательной функцией для современных веб-приложений. Это изначально подходит для… Помечено как java, учебное пособие, webdav, облако.

Аутентификация с помощью токенов стала обязательной функцией для современных веб-приложений. Он изначально подходит для одностраничных приложений и мобильных приложений. В то время как Spring де-факто является стандартом в экосистеме Java, есть разработчики, которые предпочитают использовать альтернативные решения. Это верно, потому что Spring – довольно тяжелая платформа, и существует ряд легких и быстрых микро-фреймворков. Javelin – популярный выбор – он начался с хорошо известного проекта Spark Java. Однако микро-фреймворки предлагают разработчикам набор инструментов barre, часто ограниченный обработкой HTTP-запросов/ответов. Все остальные функции, такие как безопасность, выходят за рамки. Это означает, что мы должны реализовать это вручную.

В этой статье представлен обзор реализации аутентификации на основе токенов (JWT authentication) для REST API, написанной с помощью Javalin microframework. Существует решение , но это может быть не лучшим вариантом – в частности, если вам нужна большая гибкость или вам нужно иметь больше контроля над аутентификацией. Для этого вам может быть интересно продолжить чтение этого поста.

Архитектура приложений

В этом посте мы рассмотрим простое приложение для управления задачами. Он организован в виде REST API и имеет две основные группы конечных точек: auth (регистрация/вход в систему) и задачи. защищенная/задачи конечная точка защищена с помощью JWT – пользователь должен прикрепить заголовок Authorization с действительным токеном и идентификатором пользователя. Я не буду подробно описывать каждый компонент, так как вы сами можете получить доступ к полному исходному коду в это репозиторий github . Вместо этого мы сосредоточимся на том, как работать с токенами и внедрять конечные точки аутентификации. Взгляните на приведенный ниже график, который демонстрирует архитектуру приложения:

Как я уже упоминал ранее, в этой статье мы рассмотрим:

  • UserController компонент (обрабатывает HTTP-запросы)
  • Пользовательский сервис и его реализация (содержит бизнес-логику, связанную с потоком аутентификации)
  • Менеджер токенов и его реализация (компонент, который генерирует и утверждает токены).

Генерировать токены

В этом посте мы будем использовать библиотеку jjwt для работы с токенами. Альтернативным решением является использование на Nimbus Jose-JWT – вы можете узнать больше в моем посте о двухфакторной аутентификации для Webflux , где я ее использовал. Чтобы генерировать токены, нам сначала нужно иметь секретный ключ. Обычно ваше приложение считывает его из конфигурации, но в примере мы можем полагаться на генерируемое значение:

public JjwtTokenManagerImpl() {
    this.key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
}

Процесс выпуска новых токенов очень прост с помощью jjwt library. Все, что вам нужно, это указать те параметры, которые вы хотите включить в полезную нагрузку токена. В простейшем случае это будет просто userId значение, хотя вы можете добавить другие утверждения :

@Override
public String issueToken(String userId) {
    String token = Jwts.builder().setSubject(userId).signWith(key).compact();
    return token;
}

Проверка токенов

Чтобы получить доступ к защищенным маршрутам, пользователь должен предоставить два заголовка: токен и идентификатор пользователя. Следовательно, Token Manager.authorize() метод отвечает за утверждение токена.

@Override
public boolean authorize(String token, String userId) {
    try {
        String subject = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody().getSubject();
        return subject.equalsIgnoreCase(userId);
    } catch (Exception ex){
        throw new ForbiddenResponse();
    }
}

Регистрация

Сначала нам нужно создать пользователя, прежде чем он/она сможет получить доступ к API-интерфейсам задач. Для этого клиент отправляет электронное письмо и пароль на конечную точку /регистрация . Здесь нам нужно выполнить следующие шаги:

  1. Получить адрес электронной почты и пароль из тела запроса ( Запрос авторизации )
  2. Создайте нового Пользователя с электронной почтой и паролем
  3. Сохранить объект пользователя в базе данных
  4. Получить Идентификатор пользователя
  5. Выдать новый токен для этого пользователя
  6. Возврат клиенту успешен authResponse объект со значением токена и идентификатора пользователя

Для части контроллера нам нужно реализовать шаги 1 и 6, в то время как шаги 2-5 обрабатываются службой. Взгляните на фрагмент кода, который содержит код UserController.signup() flow:

public void signup (Context context){
    AuthRequest request = context.bodyAsClass(AuthRequest.class);
    AuthResponse result = service.signup(request);
    context.json(result);
}

Обратите внимание, что Javelin предоставляет объект Context для абстрактной работы с HTTP-запросами/ответами. В нем также есть такие методы, как body As Class() для сериализации тела JSON в Java entity и json() для отправки ответа с данными JSON. Далее давайте проверим, как выполнить шаги 2-5 в UserServiceImpl реализация. Вот код:

@Override
public AuthResponse signup(AuthRequest request) {
    String email = request.getEmail();
    String password = request.getPassword();
    User user = repository.signup(email, password);
    String userId = user.getUserId();
    String token = manager.issueToken(userId);
    AuthResponse response = new AuthResponse(userId, token);
    return response;
}

Теперь вы можете запустить приложение и проверить, работает ли процесс регистрации:

Авторизоваться

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

  1. Получить адрес электронной почты и пароль из тела запроса ( Запрос авторизации )
  2. Найти в базе данных a Пользователь с тем же адресом электронной почты
  3. В случае, если этот пользователь действительно существует – подтвердите что пароли действительно совпадают
  4. Выдать токен
  5. Возврат клиенту успешен authResponse объект со значением токена и идентификатора пользователя
  6. Если пользователь не существует или пароли не совпадают – верните authResponse с

Опять же, мы разделяем процесс на две части: контроллер задерживает HTTP-запрос/ответ и службу для выполнения бизнес-логики. Взгляните на реализацию контроллера ниже:

public void login (Context context){
    AuthRequest request = context.bodyAsClass(AuthRequest.class);
    AuthResponse result = service.login(request);
    context.json(result);
}

Как правило, код контроллера выглядит аналогично предыдущему, поскольку он выполняет только обработку запросов/ответов. Шаги 2-4 определены в части сервиса. Вот как это делается:

@Override
public AuthResponse login(AuthRequest request) {
    String email = request.getEmail();
    String password = request.getPassword();
    Optional result = repository.findByEmail(email);
    if (result.isPresent()){
        User user = result.get();
        String passwordInDatabase = user.getPassword();
        if (password.equalsIgnoreCase(passwordInDatabase)) {
            String userId = user.getUserId();
            String token = manager.issueToken(userId);
            AuthResponse response = new AuthResponse(userId, token);
            return response;
        } else {
            throw new ForbiddenResponse();
        }
    } else {
        throw new ForbiddenResponse();
    }
}

Взгляните на этот фрагмент кода. Вы могли бы отметить, что если пользователь не представлен или был введен неправильный пароль, приложение прерывает поток с помощью ForbiddenResponse . Это особый тип исключений из Javelin, которые сопоставляются с кодами ответов HTTP и упрощают разработку. Например, давайте попробуем войти в систему пользователя с неправильным паролем:

Наконец, мы можем выполнить тестирование , чтобы убедиться, что процесс входа в систему работает так, как мы планировали. Позвольте вызвать login конечную точку с действительными учетными данными:

Все запущено и работает! Вот и все для этого поста. Вы можете найти полный исходный код, включая реализации защищенных конечных точек, в этом репозитории github . Не стесняйтесь экспериментировать с ним. Если у вас есть вопросы относительно этого поста, вы можете задать их в комментариях ниже или связаться со мной.

Оригинал: “https://dev.to/iuriimednikov/home-made-jwt-authentication-for-javalin-2pen”