Как реализовать фильтр для получения текущего пользователя и идентификации в Redis
Что проверяют
Умеете ли вы встроить аутентификацию в цепочку фильтров Spring так, чтобы она была потокобезопасной, быстрой и правильно отвечала при ошибке. Redis здесь хранилище сессий или токенов. Следом спрашивают, чем такая схема отличается от JWT и как отозвать сессию.
Как отвечать
Каркас на Spring Boot 3 и новее:
- фильтр: наследник
OncePerRequestFilter, чтобы он отработал один раз на запрос. Регистрация в цепочке черезhttp.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class); - извлечение токена: заголовок
Authorizationс префиксомBearerили кука. Некорректный заголовок это 401, а не исключение наsplit; - поиск в Redis: ключ вида
session:<sha256 токена>, значение хэш или JSON сuserIdи ролями. Хранить хэш токена, а не сам токен, безопаснее: утечка дампа Redis не даст готовых сессий; - скользящий TTL:
GETEXс новым сроком (Redis 6.2 и новее) продлевает жизнь сессии тем же запросом, что и читает её; - контекст: создаёте
UsernamePasswordAuthenticationTokenс ролями и кладёте вSecurityContextHolder. Контроллеры получают пользователя через@AuthenticationPrincipal, а не через атрибуты запроса; - клиент:
StringRedisTemplateповерх Lettuce, клиента по умолчанию в Spring Data Redis. Он потокобезопасен.
Если нужна просто серверная сессия, назовите Spring Session Data Redis: он делает то же самое без ручного фильтра. Отзыв в такой схеме тривиален: удалили ключ, и следующий запрос получит 401, в отличие от JWT, который живёт до истечения срока.
Где спотыкаются
Держат один экземпляр Jedis в поле фильтра. Он не потокобезопасен, а фильтр обслуживает запросы параллельно: под нагрузкой протокол ломается, появляются ошибки чтения и чужие ответы. Нужен JedisPool или Lettuce.
Помечают фильтр @Component и не отключают авторегистрацию. Spring Boot добавляет любой бин Filter в сервлет-контейнер, и фильтр срабатывает ещё и вне цепочки безопасности, в другом порядке. Лечится FilterRegistrationBean с setEnabled(false).
Отвечают на неудачу текстом «Unauthorized» со статусом 200 или берут javax.servlet, хотя с Spring Boot 3 пакеты перешли на jakarta.servlet.