Java: fill the gaps
12.9K subscribers
7 photos
215 links
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк

🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt

Комплименты, вопросы, предложения: @utki_letyat
Download Telegram
Какой паттерн на практике считается альтернативой паттерну Saga?
Anonymous Poll
34%
Message router
13%
Claim check
14%
Service activator
21%
Retry
18%
Circuit breaker
🔥35
Сага

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

Какую проблему решаем?

В системе много компонентов: стайка микросервисов, несколько БД, кэши и месседж брокер. Каждый микросервис в норме работает с одной БД и имеет четкий круг обязанностей.

Для многих задач нужно пройтись через несколько сервисов и зафиксировать изменения в нескольких БД.

Пример — покупка в интернет-магазине. Создать заказ, проверить наличие, снять деньги и тд. Если на каком-то шаге случится ошибка, предыдущие изменения можно откатить, чтобы система осталась в согласованном состоянии.

Для таких случаев на сцену выходит паттерн сага в двух вариантах: хореография и оркестрация.

💃 Хореография — каждый сервис “танцует” свой набор движений: знает, что делать при ошибке и кого предупредить. Например:
▫️ В сервисе А происходит ошибка
▫️ Сервис А шлёт сервису Б событие “я не смог”
▫️ Сервис Б дёргает у сервиса В команду “отмена”
▫️ Система снова в согласованном состоянии🥳

Плюсы:
Легко сделать для простых бизнес-процессов

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

🥁 Оркестрация — когда за порядком следит отдельный сервис. Если кто-то не смог, оркестратор шлёт остальным участникам компенсирующие указания.

Плюсы:
Сервисы меньше знают друг о друге, и логика внутри проще. Сервис оплаты не придумывает, что делать при отмене заказа, а получает четкую команду “Верни деньги”
Информация о шагах находится в одном месте
Легче понять, что происходит и разобраться в ошибках
Проще менять шаги в процессе
Проще в управлении, если в системе несколько сложных процессов

Минусы:
Ещё один компонент. Дополнительный деплой, мониторинг, страничка в конфлюенс и тд

Как реализуется оркестрация?

Есть миллион библиотек и фреймворков, например, Apache Camel или Netflix Conductor. Упомянете их — получите плюсик.

Почему сага редко встречается?

Система не выполняет в итоге нужное действие. Чаще проблема решается через Retry или Fallback. Откат предыдущих шагов — очень крайняя мера.

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

Но в обсуждении саги есть важный нюанс, который меняет все. Если его не понимать, напротив вашей кандидатуры появится жирный минус.

Расскажу в следующем посте, а сейчас жду ваших огоньков🔥
🔥794👍5320👎3
Распределенные транзакции

Кукусики, спасибо за огоньки к прошлому посту! Очень приятно, вижу, что цените качественный контент🥰

А теперь к делу. В посте про сагу мы сформулировали, что она нужна для распределенных транзакций. Если на собесе упоминается термин “распределенная транзакция”, грех не спросить, что же это такое.

Удивительно, но на этом шаге многие сыпятся🤷

Транзакция — очень многозначительное понятие в айтишке, но первое, что приходит на ум — транзакции в базе данных.

Соответственно, распределённая транзакция — набор изменений в нескольких БД, которые выполняются атомарно. В любой момент времени система находится в согласованном состоянии.

— И как это, хорошо или плохо? — спрашивают вас
Чувствуете подвох, но пока не понимаете где:
— Да нормально вроде…

Интервьюер понимает, что вы опасны для проекта и ставит жирный минус.

Что не так с распределенными транзакциями в БД?

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

Каждый сервис выполняет задачи определенного типа. Сервис заказов работает с заказами, сервис логистики — с маршрутами и тд. Все нужные сервису данные обычно лежат в одной БД.

🚩 Распределённая транзакция в БД — это обычно знак, что мы неверно распределили функции по сервисам.

Второе значение слова транзакция — набор изменений, которые происходят атомарно. Либо все завершатся успешно либо ни одно.

Тут нет отсылок на БД, нет требования одновременности и согласованности в каждый момент времени. И это уже нормальный вариант для жизни. Если что-то пошло не так, делаем ретрай или потихоньку откатываем изменения.

Ещё раз подсвечу разницу.

🛢️ Распределённая транзакция в БД:
▫️ Данные в системе согласованы в каждый момент времени
▫️ Гарантия распространяется только на изменения в БД
▫️ Сложно и долго
▫️ Признак плохой архитектуры

🎩 Распределённая транзакция бизнес-процесса:
▫️ Eventual consistency. Однажды всё станет хорошо, но в процессе возможны несогласованные данные
▫️ Можно менять состояние не только в БД, но и в других сервисах, файлах, отправлять сообщения в месседж брокер
▫️ Не требует сложных блокировок

Термин один, а значения абсолютно разные! Обидно, когда кандидат их путает и предлагает масштабно блокировать систему, где это не требуется.
🔥220👍5015👎9
Посолить, поперчить по вкусу

Пароли пользователей нужно беречь. Для защиты используется целый комплекс мер: от безопасной передачи по сети до шифрования данных на уровне БД.

Сегодня поговорим о способе защиты, с которым может встретиться любой бэкендер — соли🧂. Обсудим, что это и зачем она нужна.

Начнём с основ. Почему нельзя хранить пароль как обычный текст?

🥷🏼 Любой человек с доступом к БД сделает select, запишет логин-пароль и никто об этом не узнает
🥷🏼 Злоумышленник найдёт бэкап БД и прочитает данные

Первый логичный шаг — хранить не сам пароль, а его хэш. Операция однонаправленная: проверить введённый пароль можно, а вот получить пароль из хэша нельзя.

Почему хэш — это недостаточно безопасно?

Злоумышленник может воспользоваться rainbow tables🌈. Это база, где хранятся миллиарды хэшей разных строк. Это не какой-то даркнет, есть много сайтов с таким функционалом. Например, этот.

И здесь на сцену выходит наш главный герой🦸‍♂️

Соль — это последовательность символов, которая добавляется к паролю перед расчётом хэша:
Пароль: qwerty
Соль: 34%$b
В БД записываем хэш от qwerty34%$b

Если злоумышленник узнал хэш пароля, вероятность, что этот хэш найдётся в rainbow table ощутимо снижается.

Соль генерируется случайным образом, у каждого пользователя она своя. Может хранится вместе с паролем или в отдельном поле в БД.

🌶 Pepper

При доступе к БД можно прочитать и засоленный пароль, и саму соль. Восстановить исходный пароль сложно, но если очень хочется, то можно. Чтобы усилить защиту, используется вариация под названием pepper (или secret salt). В этом случае последовательность символов, которая добавляется к паролю, хранится не в БД, а в другом месте.

Spring Security

К счастью, Spring предлагает готовые энкодеры, которые упрощают манипуляции с солью:

🔸 BCryptPasswordEncoder
генерирует соль и хранит её в итоговой строке. Разработчику ничего делать не надо, всё работает само:)

🔸 Pbkdf2PasswordEncoder
тоже сам разбирается с солью, плюс можно передать pepper в конструкторе объекта.

Ответ на вопрос перед постом: соль помогает в случае, когда злоумышленник прочитал из базы поле password. Она снижает вероятность, что по хэшу можно восстановить исходный пароль.
🔥182👍6022👎3
Как быстро отдавать данные по частям

Пагинация с первого взгляда кажется простой задачей. На практике это та ещё кроличья нора🌚

В этом посте расскажу 2 подхода к пагинации со стороны SQL, их плюсы, минусы и основные кейсы. За кадром оставим нюансы спринга и хибернейта.

Итак, пагинация бывает двух видов: offset и keyset.

🌸 Offset - для получения части данных используется (сюрприз) оператор offset. Первую страницу получаем так:
SELECT * FROM t ORDER BY …
LIMIT 100

Для второй и последующих добавляем offset:
SELECT * FROM t ORDER BY …
LIMIT 100 OFFSET 100

Offset показывает, сколько строк пропустить.

В чем подвох: кажется, что при offset 100 постгрес сразу прыгнет на 101 строку, но это не так. Он честно пройдет 100 строк, и потом отдаст следующие 100.

В случае большого оффсета такая операция будет работать долго даже при наличии индекса
Просто реализовать. Минимум действий со стороны клиента и поддержка в фреймворках.

🌸 Keyset пагинация. Она же cursor, она же seek.

Здесь мы используем where в паре с индексом и сразу прыгаем на нужную позицию. Первую страницу получаем так:
SELECT * FROM t ORDER BY name
LIMIT 10

Если записи отсортированы по имени, запоминаем имя последнего элемента. Пусть это будет х. Чтобы получить вторую страницу, добавляем where:
SELECT * FROM t ORDER BY name
WHERE name > x
LIMIT 10

Работает быстрее, чем вариант с оффсетом. Чем больше оффсет, тем значительнее разница
Сложнее в реализации, надо запоминать поле в последней пачке
Нельзя перейти к произвольной странице, только последовательная выдача данных

Когда что использовать?

Во многих докладах/статьях часто встречается мысль, что оффсет это ужас, и надо всегда использовать кейсет. На практике все не так категорично:

▫️ Если на сайте infinite scrolling или пользователь вряд ли будет много смотреть, вполне ок использовать простую оффсет пагинацию.

▫️ Если данные выкачиваются большими пачками для обработки, аналитики, миграции и тд, берём keyset пагинацию.

▫️ Если надо выгрузить много данных и прыгать по ним туда-сюда или у вас другой сложный случай, придется экспериментировать с индексами и запросами. Этот доклад хорошо показывает, что пагинация - далеко не тривиальная задача.

А поставить огонек качественному контенту очень просто, поэтому это нужно обязательно сделать🔥
🔥624👍4724👎7
Вышла Java 23🎉

Это не LTS версия, почти все фичи в превью стадии. Этап важный, но не самый интересный.

Поэтому подниму другую тему.

Я уже несколько лет делюсь знаниями в этом канале. У меня хорошо получается видеть главное и подсвечивать неочевидные моменты. Хочу продолжать в том же духе.

Помогите мне с контент-планом:)

В чем вам помочь разобраться? Какие знания хочется углубить?

Заполните, пожалуйста, эту форму. Всего 3 вопроса!
🔥18634👍31
Как освоить многопоточку?

Спасибо всем, кто поделился мнением в предыдущем опросе! Очень много интересных идей и комплиментов🥰

Но есть момент, который меня удивил — огромное количество запросов про многопоточку!

С одной стороны, это понятно. Тема сложная, толковых материалов мало, гайдов по практике вообще нет.

С другой стороны, решение же давным-давно висит в закрепе!

1️⃣ Бесплатный курс по основам многопоточки

Мини курс по базовым темам с тестами и задачками. Такой формат гораздо полезнее постов.

Ещё раз, абсолютно бесплатно!

2️⃣ Полный курс по java.util.concurrent

Для тех, у кого многопоточка на проекте, и кто хочет отвечать на собесах не только на примитивные вопросы

Много нюансов и практики, разбираем код реальных проектов.

Полностью закрывает вопрос с многопоточкой
Можно оплатить за счёт компании
Можно вернуть 13% через налоговый вычет
Есть рассрочка

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


Так что с многопоточкой всё давно решено, все ссылки в закрепе.

Остальные ответы ещё разгребаю, буду писать посты по мере сил. Ещё раз спасибо, очень ценю вашу помощь❤️
109👍47🔥31👎5
GraalVM, часть 1

Последние годы на конференциях встречаются доклады про GraalVM. У них мало просмотров, тк есть мнение, что Грааль — что-то экспериментальное и далёкое от коммерческой разработки.

Но нет!

GraalVM — проект, который объединяет в себе целую смесь идей и технологий. В этом посте расскажу о самом близком к практике и понятном компоненте — JIT-компиляторе Graal и зачем он нужен.

Начнем с основ:
✍️ Программист пишет java код
✍️ Компилятор превращает его в байт-код
✍️ JIT-компилятор внутри JVM переводит байт-код в системные вызовы для конкретной ОС

JVM использует два типа компиляторов: С1 и С2:
🐍 С1 (client) — для десктопа, браузера и приложений на слабом железе. Быстрый старт, низкое потребления памяти, но мало оптимизаций
🐊 С2 (server) — для больших и мощных серверов. Алгоритмы сложнее, памяти нужно больше, зато на выходе более оптимизированный нативный код

Разделение было актуально 20 лет назад. Сейчас железо мощнее и дешевле, поэтому с java 8 работает смешанный тип (tiered compilation) с участием обоих компиляторов.

Технологии позволяют сделать компиляцию быстрее, но вносить серьезные изменения в С1 и С2 проблематично, так как код сложный и запутанный.

Тот случай, когда проще написать заново:)

В Java 9 работа с JIT-компилятором скрылась за отдельным интерфейсом JVMCI — JVM Compiler Interface. Теперь можно собрать JVM с другим компилятором. Например, с новым и свежим JIT компилятором Graal.

В OpenJDK включается флажками
-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

Работает пока только для Linux, иногда показывает небольшой прирост скорости. Так что имеет смысл попробовать!

В GraalVM есть ещё одно интересное направление, и о нем расскажу в следующем посте🔥
🔥178👍4918👎4
GraalVM, часть 2: native image

Продолжаем вникать в GraalVM. Сегодня поговорим о самой интересной его части — native image билдер.

Он компилирует java код и все нужные классы сразу в бинарный файл. Нет промежуточного этапа в виде байткода, а значит для запуска не нужна JVM.

Джава код запускается без JVM🤯

Бинарник собирается под конкретную платформу, поэтому код больше не write once, run anywhere! Одна из киллер фич java отправляется на антресоль.

Что получаем от такого богохульства:
Быстрый старт, тк все классы уже загружены
😐 Немного усложняется CI и тестирование
😡 Нужно адаптировать код

Например, статические блоки по классике выполняются при загрузке класса. Если класс не загружается, блок не выполнится.

Это легко чинится переносом кода из статики в другое место. Таких нюансов довольно много, но почти для всех есть обходные пути

😡 Если приложение использует библиотеки, они тоже должны предоставлять нейтив вариант
😡 Долгая компиляция

Сама с native images не сталкивалась, но видела доклад, как их используют в одном из проектов Сбера. Так что вещь рабочая, хоть и специфичная.

Spring native image🍃

Интересный проект по адаптации Spring под нейтив имедж. Обычные спринг приложения стартуют несколько минут. Ожидается, что native вариант запустится за несколько секунд!

Сложность в том, что в спринге большая часть магии происходит на старте. Активно используется рефлекшн и создаются прокси-классы. Чтобы адаптировать Spring под native, надо переписать огромную часть кода.

Плюс не все библиотеки легко адаптируются под нэйтив. Например, сейчас нет поддержки Mockito. А куда мы без него?

Поэтому сохраняется интрига — непонятно, что в итоге получится, и стоит ли оно того. Но следить за этим очень интересно🥰

Подведем итог

GraalVM — проект, объединяющий несколько направлений:
🌸 JIT компилятор. Уже сейчас можно запустить на OpenJDK и OracleJDK и чуточку ускорить приложение
🌸 Виртуальная машина GraalVM
🌸 Сборщик native image. Интересен и сам подход, и проекты, которые его используют, например, Spring native image

Есть в GraalVM и другие штуки, например, мультиязычный рантайм. Но они вряд ли пригодятся большинству из нас, поэтому не будем о них😊
🔥111👍4413👎4
Какая стратегия деплоя требует больше всего ресурсов в инфраструктуре?
Anonymous Poll
16%
Rolling update
36%
Blue-Green
14%
Canary
18%
A/B testing
15%
Shadow deployment
👍36
Стратегии деплоя

– забота не только девопсов, но и разработчиков уровня сеньор+. В этом посте обозначу, какие схемы бывают, и в чем их суть.

Все сценарии работают на одну из двух ситуаций:

Ситуация 1: нужно обновить сервисы на проде с версии 1 на версию 2

🌔 Rolling update

Добавляем инстанс с версией 2 и переводим туда часть запросов. Если все ок, понемногу добавляем сервисы с версией 2 и убираем с версией 1

👯 Blue-green

У нас всегда в наличии 2 идентичных продакшн среды. На одной функционирует версия 1 и обрабатывает все запросы, другая простаивает.
▫️ Раскатываем сервисы версии 2 на неактивный стенд
▫️ Переключаем роутер с одного стенда на другой

На практике чаще встречается Rolling update, потому что это дешевле.

Ситуация 2: хотим протестировать фичу в продакшене😇

🎏 AB testing

▫️ Запускаем несколько экземпляров сервиса с новой фичей и направляем туда часть запросов
▫️ Сравниваем метрики старой и новой версии
▫️ Выбираем победителя
▫️ Раскатываем его на всех пользователей

Способ часто используется для изменений на фронте.

🐤 Канареечный деплой (canary)

Поднимаем сервис с новой версией, переводим туда трафик от определенной группы пользователей. Если все ок, раскатываем новую версию дальше.

Технически похоже на rolling update, но в случае канарейки мы явно указываем, каким пользователям видна новая версия. Это могут быть бета тестеры или представители заказчика.

Используется такой способ редко, лично ни разу не встречала.

🌚 Shadow deployment

▫️ Поднимаем сервис с новой версией
▫️ Дублируем туда трафик с продакшена
▫️ Смотрим, как сервис справляется (или нет🥲)

Цель shadow deployment - проверить фичи на бэкенде под реальной нагрузкой. Иногда поднимается полная копия продакшен среды. Но чаще все же ограниченный набор сервисов.

Как реализовать эти стратегии?

Основную работу делают девопсы с помощью bash скриптов, Kubernetes, Consul, Istio и их аналогов.

Со стороны разработки может понадобиться
▫️ добавить механизмы включения/выключения фич (feature toggle)
▫️ обеспечить совместимость старой и новой версии по данным и API. На практике бывает сложно, по миграциям данных даже целые книжки пишут!

Ответ на вопрос перед постом – больше всего ресурсов требует Blue-green стратегия. Также принимается вариант с shadow deployment на максималках🔥
🔥135👍4921👎1
Какой хэш отличается от остальных?
👍11
Какой хэш отличается от остальных в коде выше?
Anonymous Poll
9%
hash1
5%
hash2
50%
hash3
15%
Все хэши разные
21%
Все хэши равны
👍81
Критикую JDK: метод hashCode

Сегодня на простом примере из JDK покажу, как НЕ нужно проектировать апи. Обычно всю теорию по этой теме можно свести к “делайте хорошо, а плохо не делайте”. Разбор ошибок - очень наглядный способ посмотреть все на практике.

Поэтому углубимся в два метода класса Objects:
Objects.hashCode(Object)
Objects.hash(Object…)


Человек, который видит их в первый раз, обязательно спросит
🤔 Чем отличается hash и hashCode?
🤔 Почему они выдают разные результаты?


(ответ на вопрос перед постом: hash3 отличается от остальных хэшей)

В хорошем апи у пользователя минимум вопросов, как им пользоваться. В идеале даже смотреть документацию на нужно. Это, наверное, самый главный принцип проектирования.

В нашем случае приходится лезть в доку и исходный код👎

Что увидим внутри:
▫️ hashCode принимает 1 аргумент - Object, и возвращает его хэш
▫️ hash принимает массив объектов переменной длины, итоговый хэш считается на основе переданных полей. Чтобы учесть порядок, hash производит дополнительные вычисления, даже для одного поля. Поэтому итоговые хэши отличаются.

Супер, я разобралась, но зачем мне в это погружаться? Я хочу просто посчитать хэш объекта, а не вникать в разницу 2 методов. Тратить даже 5 минут на такую простейшую задачу отвратительно. Bad user experience во всей красе.

В случае хэшей исправить ситуацию элементарно, просто дать методам одинаковые имена:
Objects.hashCode(Object)
Objects.hashCode(Object…)


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

Почему так не сделано изначально?

Я пыталась найти ответ, смотрела старые исходники и доки. Не нашла ни одной причины, почему имена разные и результаты не согласованы.

Видимо автор решил, что и так сойдёт.

Оставим это на совести разработчиков JDK, а себе заберём следующие выводы:
▫️ В хорошем апи пользователю не нужно смотреть документацию, чтобы сделать базовые вещи
▫️ Если в апи есть неочевидные моменты, их надо поправить, а не писать в доке warning
▫️ Если пользователь пошел смотреть исходники, чтобы разобраться - это полный провал

Чем сложнее система, тем важнее вкладываться в простоту. Простоту понимания, чтения, поддержки. Даже собственный код через пару месяцев выглядит как чужой. Понятные названия переменных, классов и методов, непротиворечивость - база для комфортной работы❤️

Это уже не первый пост, где я разбираю неудачные методы в JDK. Если вам хочется больше, предлагаю почитать
🤦 Критикую метод HashMap из java 20
🤦 Критикую методы StreamAPI в java 22
🤦 Критикую методы BigDecimal
👍177🔥7124👎17
Spring Security: основная архитектура

Разгребала на выходных опрос по непоняткам в спринге. Один из самых частых вопросов касается Spring Security.

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

К счастью, с годами ситуация прояснилась. Секрет успеха по работе с Security лежит в понимании двух вещей:

🔶 Используемый механизм безопасности

LDAP, JWT, разные схемы oAuth и SSO. Роли, белые/черные списки, секреты, токены и тд. В чистом виде большинство механизмов несложные, на ютубе отлично объясняются за 10 минут.

🔶 Базовая архитектура Spring Security

Об этом и будет пост. Опишу простыми словами, что происходит в классической (не реактивной) архитектуре, и как решать задачки с секьюрити на практике.

Spring Security участвует в двух процессах:

1️⃣ Путь запроса до контроллера и обратно

Схема такая:
▪️ Сервер получает запрос
▪️ Проводит его через цепочку фильтров, у каждого из которых своя задача:
    ▫️ CsrfFilter проверяет CSRF токен
    ▫️ SessionManagementFilter разбирается с сессией
И так далее, обычно 10-15 фильтров в итоге
▪️ Запрос попадает в контроллер
▪️ Выполняется бизнес-логика проекта
▪️ Контроллер возвращает ответ
▪️ Ответ проходит по той же цепочке фильтров, но в обратном порядке. Фильтры выставят у ответа нужные заголовки, обнулят/сохранят сессию и тд
▪️ Ответ возвращается пользователю

Что тут важно:
▫️ Проверка прав, заголовков и многие секьюрити штуки часто обрабатываются в фильтрах ДО вызова контроллера
▫️ Можно поменять содержимое, заголовки и другие части ответа ПОСЛЕ выхода из контроллера
▫️ Каждый фильтр может менять запрос/ответ или контекст, поэтому порядок фильтров важен
▫️За конфигурацию фильтров отвечает метод конфига
WebSecurityConfigurerAdapter#configure(HttpSecurity http)

Чтобы увидеть процесс наглядно, поставьте брейкпойнт в методе
FilterChainProxy#doFilterInternal. Там увидите все фильтры, можно погулять по ним и отследить, что происходит с запросом

2️⃣ Авторизация/аутентификация
скрывается за фасадом — бином AuthenticationManager.

Чтобы авторизовать пользователя, в коде проекта нужно вызвать метод
authenticationManager.authenticate

Может в фильтре, может в контроллере, зависит от механизма безопасности.

Метод authenticate пройдётся по всем заданным источникам авторизации. Результат можно положить в SecurityContextHolder, тогда он будет доступен из любого места в коде. Также бросится событие AuthenticationSuccessEvent, но обычно его игнорируют:)

Источники авторизации называются *Provider (например, DaoAuthenticationProvider) и определяются в конфиге. В итоге всё, что нужно от разработчика это:

☝️определить провайдеры
✌️ вызвать authenticate в  нужном месте

Посмотреть список текущих провайдеров: брейкпойнт в методе  ProviderManager#authenticate.

Задать список провайдеров: метод configure(AuthenticationManagerBuilder auth) в классе WebSecurityConfigurerAdapter

🦄Что с этим знанием делать на практике

Если вам дали задачу, связанную с Spring Security, не бросайтесь сразу гуглить

spring security jwt/oauth/ldap example

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

Лучше проработать каждую часть по порядку:

🛡️ Разобраться в механизме безопасности, который нужно реализовать. Что, откуда и как передаётся, где, когда и как валидируется и сохраняется.

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

🛡️ Прикинуть, как полученная схема ложится на архитектуру секьюрити. Что сделать в фильтрах до обработки запроса, а что после. Возможно, что-то надо сделать внутри слоя сервисов

🛡️ Желательно обсудить полученное решение со старшим товарищем

Только после этого смотрите документацию, туториалы и собирайте из них решение.

Действуя по плану выше, вы потратите меньше времени и сохраните высокую самооценку🙂
👍209🔥9343👎5
Зачем нужен и чем плох Optional.stream()?

Когда ты в разработке 10+ лет, сложно чему-то удивиться. Кажется, что JDK изучен вдоль и поперек, и этот мир абсолютно понятен.

Но недавно мне встретился странный зверёк метод, и это вылилось в очередной пост серии “критикую JDK”.

Допустим, вы увидели в коде Optional.stream(). Попробуем угадать, что он делает.

Может быть, метод нужен, чтобы сделать из Optional стрим и использовать методы Stream API?

Хотя интернет предлагает нам статьи с подобными примерами, включим здравый смысл.

Зачем так усложнять код? Зачем из одного значения делать стрим?
Оставим авторов статей молча краснеть и продолжим исследование.

📚 В документации находим больше интересных деталей:

▫️ Если в Optional есть значение, метод вернёт стрим с этим значением.
▫️ Если Optional пустой, вернётся пустой стрим.

Также узнаем, что метод предназначен для работы с flatMap.

Например, некоторые пользователи указывают емейл, а некоторые нет. Если getEmail возвращает Optional, список емейлов получаем так:
List emails = users.stream().map(User::getEmail).flatMap(Optional::stream).toList();

А теперь критика. Ваша любимая часть:) Что с этим методом не так, и как можно сделать лучше?

Метод нужен, чтобы вытащить данные из набора Optional. В одну строчку это не сделать, минимум в две:
.filter(Optional::isPresent)
.map(Optional::get)

Новый метод делает все в одну строку, но

Почему. Такой. Ужасный. Дизайн.

👎 Метод находится в классе Optional, но бесполезен для Optional обьекта
👎 Метод заточен только под flatMap
👎 Выглядит непонятно. flatMap(Optional::stream) похож на набор слов, приходится напрячься, чтобы его понять. Сравните с очаровательными filter, map, count, findFirst🥰
👎 Сомнительная реализация. Сначала плодим маленькие стримы из каждого элемента, потом их соединяем. Столько лишних действий!

Как можно по-другому?

Я бы добавила в Stream API отдельный метод, “очищающий” текущий стрим от лишнего. Пусть он будет универсальным:

▫️ Для стрима из Optional возвращает стрим значений, empty пропускаются
▫️ Для прочих стримов убирает null элементы

Название под вопросом. Мне нравится getValues(), но можно и получше придумать.

И всё, незачем усложнять класс Optional и плодить сложные конструкции!

Простота и ясность - наше всё. Не знаю, чем думал автор метода и куда смотрели ревьюеры. Мы с вами точно бы такое не пропустили🔥
🔥177👍76👎4925
Любимые подписчики!

Конец года - прекрасное время для благодарностей и подведения итогов. От всей души, спасибо, что читаете❤️ Спасибо за ваш интерес к разработке и повышению знаний. На таких людях наша сфера держится и развивается!

Лучшие посты этого года:
Паттерн Сага
Как быстро отдавать данные по частям
Полезное в PostgreSQL
Новая фича Java 23: Derived Record
Как считается хэшкод по умолчанию?
Нужно ли высшее образование для разработчика?
Как вредит ChatGPT
От JDBC до Spring Data

Почитайте, если что то пропустили. Или не читайте:)

Желаю вам хорошо отдохнуть на каникулах и выспаться, а в следующем году покорить новые профессиональные и финансовые высоты!

С наступающим новым годом, друзья🎄
180🎄104🔥40👍11👎1