Pull to refresh
66
0
alek_sys @alek_sys

User

Send message

Статья как старый анекдот - правда, но не совсем. "Не миллион, а тысячу, не в лотерею, а в карты, не выиграл, а проиграл".

Правительство Великобритании приняло постановление, согласно которому Подразделение цифровых рынков (Digital Markets Unit, DMU) имеет право контролировать деятельность крупных IT-компаний и привлекать их к ответственности за несоблюдение правил приемлемого поведения с конкурентами и клиентами.

И здесь так же - не правительство, а конкретно DCMS, не приняло, а начало консультацию, которая продлится до октября, не имеет, а может быть будет иметь. Даже по окончанию консультации, это должно стать bill в парламенте, пройти все чтения, пройти утверждение Палатой Лордов и потом, когда-нибудь, может быть, стать законодательным Актом Парламента. В каком виде и когда это дойдет до акта - не понятно. Может, и вообще не дойдет.

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


Из коробки JWT прекрасно поддерживается в Spring Security.


Я уже насколько много прочитал статей и вопросов "Spring Security + JWT + stateless", что рискну предположить — "не поддерживается" это означает "нельзя при Form Login вместо сессии использовать JWT из cookie"? Это как раз самописная реализация подмножества протокола, под нее загнуть Spring Security можно, но я бы трижды подумал. Там раскидано много граблей, надо точно знать куда ступать. Если же уверенности в хождении по граблям нет, то я бы советовал строго следовать протоколу OpenID, тогда Spring Security будет работать прекрасно.


Классическая архитектура для stateless безопасности, чтобы не писать ее самому это:


  1. Authorization Server (либо внешний Auth0 / Okta / etc, либо on premise Hydra или типа того, можно даже Spring Authorization Server посмотреть). Это и будет единой точкой аутентификации — и мобильное приложение, и сайт для доступа по REST API будут ходить сюда. Только тут живет информация о пользователях — логины, MFA, пароли, профили.


  2. Resource Server — это тот самый REST API, Spring Security легко сконфигурить, нужно только указать issuer-uri.


  3. SPA которое использует OAuth2 Implicit Grant, получает токены из Auth-z сервера и отправляет в Authorization header в Resource Server.


  4. Мобильное приложение использует PKCE + Authorization Code через тот же Auth-z server.



В таком подходе 2-4 будут полностью stateless и не знать ничего о пользователях, кроме токена. Вся работа с аутентификацией будет в Authorization Server (1), и чаще всего если его и надо будет масштабировать, то там будут свои решения у каждого сервера. Во многих случаях его можно будет даже оставить stateful, при правильной настройке Token expiration + refresh token, логинится туда заново пользователям придется не часто.

На Resource Server нет приватного ключа, разумеется, но он не нужен. Любой клиент Authorization Server-a может спросить у него публичные ключи чтобы валидировать токен. Но в остальном да, идея такая, это стандарт JWS.


Auth-z Server (который еще ранняя альфа) и Resource Server работают по стандартам OAuth2, так что, именно так.

По-сути, вопрос стоит в параллельной поддержке OAuth2 стандарта и стандарта VK, не думаю, что Spring Security умеет это из коробки. Скорее всего тоже придется писать свою делегирующую обертку, которая для ВК будет вызывать одно, для других провайдеров другое.

Я, кажется, имел в виду что вредна конкретно в этом случае. Своя реализация UserDetailsService нужна как раз чтобы поддержать такие вот сложные и нестандартные ответы от UserInfo Endpoint. Ответ от ВК выглядит странно, такое ощущение что он не следует спецификации. Тогда да, придется писать свой сервис.


.customUSerType уже deprecated, и рекомендуется использовать свой сервис и делегацию.

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


Этот комментарий для читателей, которые наверняка захотят применить подход из этой статьи или миллиона ей подобных, которые легко гуглятся по запросу Spring Security + login + jwt и прочим.


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


Теперь более детально.


  1. Basic auth, который используется в статье, по определению stateless, ему не нужны ни сессии, ни JWT. Но часто авторы таких статей пытаются сделать Form Login + JWT. И чаще всего, это бессмысленно и бесполезно — сессия гораздо безопаснее и для form login подходит лучше (stateless безопасность за скобками).
  2. JWT токены есть смысл использовать не для first party авторизации (когда ваше же приложение и хранит пароли), а для 3rd party — когда надо аутентифицировать пользователя не зная его пароль. Для этого есть целая спецификация OAuth2 + OpenID.
  3. CSRF это не просто такой удобный способ хранить JWT токены, это фундаментально разные вещи.
  4. В ваших примерах Spring Security вообще, по сути, не нужен. Вся безопасность написана с нуля, используя кастомные фильтры.
  5. NoOpPasswordEncoder.getInstance — никогда, абсолютно никогда его не надо использовать! Даже для теста. Даже для примера.

Таких пунктов можно еще штук 10 можно написать. Это — не Spring Security, и это не Spring, так делать не надо!


Респект за использование spring-boot-devtools, очень полезная в разработке вещь!

Спасибо за работу над статьей, особенно полезны для новичков будут детали протокола OAuth2. Но и здесь есть важный момент — сам по себе OAuth2 это не протокол аутентификации, это протокол авторизации. А вот OpenID Connect это да, аутентификация, и Spring Security его отлично поддерживает.


Но все, что касается Spring Security в статье это, скорее bad practices, так делать не надо.


  1. Для начала, стоит указать версию SS — поддержка OAuth2 в 5-й версии внесена в ядро и сильно отличается от 4-й.
  2. Если используется Spring Boot, то руками создавать ClientRegistration не нужно, SB это сделает за вас. Более того, Boot из коробки поможет сконфигурировать стандартные OpenID провайдеры вроде Facebook, Google, Github, даже Token URI не надо указывать.
  3. Ну и самое главное — своя реализация UserService не просто не нужна, а даже вредна — например, тут не проверяются многие пункты стандарта, не надо так делать в реальных проектах. Вся конфигурация, в принципе, делается так: .oauth2Login(). Все, SS создаст стандартный UserService для работы с OpenID endpoint (/userinfo), если провайдер поддерживает, провалидирует ответ, создаст IdToken и вообще сделает все правильно и хорошо.

Глядя на постоянное упоминание Spring, я думаю очень важно включить в статью следующее замечание из этого гайда:


Important Technical Note

Please note that the Spring support in Quarkus does not start a Spring Application Context nor are any Spring infrastructure classes run. Spring classes and annotations are only used for reading metadata and / or are used as user code method return types or parameter types.

Т.е. используются только аннотации Spring, вся реализация выполнена самим Quarkus и не обязательно совместима.

Спасибо за перевод! Думаю, стоит так же дать ссылку на новость от самой команды Spring Boot в официальном блоге Spring.


Поддержка сканирования classpath для @ConfigurationProperties

Это уже откатили в версии 2.2.1, теперь такое поведение нужно явно включать используя @ConfigurationPropertiesScan


Автор оригинала во многом пересказывает официальный анонс, но пропускает самый первый пункт:


As part of our ongoing efforts to improve performance, we’ve made some significant progress in Spring Boot 2.2 on top of those made in 2.1. Applications will now start even faster and consume less memory while they do so. This can be particularly beneficial in environments with very tight memory constraints.

В Spring Boot 2.2 было проделано огромное количество работы направленной на уменьшение времени запуска (старт менее чем за секунду для некоторых приложений), использования памяти, нагрузки на GC.

Хорошая статья, я бы только отметил пару моментов:


  1. При наличии dev-tools не нужно перезапускать приложение, достаточно его перекомпилировать и Dev Tools обновят приложение "по горячему".


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



Basic Authentication не отвечает современному вызову угроз даже в относительно безопасной среде использования

Но реализованный подход с отправкой имени пользователя и пароля и получения токена в ответ по большому счету ничем от basic auth не отличается. JWT токен в ответе это маленькая и незначительная деталь релизации, которая ничего не меняет вообще. Цель OAuth — это как раз избавиться от необходимости предоставлять пароль third party приложениям. Текущая реализация, описанная в статье, это single-tenant (т.к. нет client-id) реализация password grant flow из OAuth2 с самодельным IdToken-ом. Так что конкретно для безопасности в production приложении, я бы все же взял Spring Security + OpenID, вместо самодельной реализации.

Из самых актуальных есть пример от самого Джо Гранжа — главного разработчика Oauth в Spring Security 5: https://github.com/jgrandja/oauth2-protocol-patterns


Там разные паттерны для OAuth показаны шаг за шагом. Это пример Джо использовал в своём выступлении на Spring I/O 2019 — но видео пока нет, организаторы обещали скоро выложить.

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


  1. Как уже упомянуто выше — проект Spring Security OAuth официально в maintainence режиме и сейчас лучше использовать Spring Security 5 который включает поддержку OAuth2.
  2. Пример когда приложение является и authorization server и resource server очень неудачный и плохо показывает зачем вообще нужен OAuth flow.
  3. Ну и это самое главное — "просто добавьте, наследуйте, создайте, определите" в обучающей статье это все же вредные советы. Зачем определять порядок фильтра? Зачем включать Global Web Security? Что делают все эти странные конфигурации и что они решают? Почему не использовать Spring Boot для всех этих дефолтов?

Лично я бы очень рекомендовал переводить статьи из качественных источников, сейчас, пожалуй, непризнанный лидер это сайт https://www.baeldung.com.

Помогла бы какая-нибудь аннотация в Junit наподобие BeforeAll

Да, ее нет, но например TestContainers выкручиваются используя static поля.

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

В ваших рассуждениях есть логическая ловушка — если заменить один бин это уже не та же конфигурация. Конфигурация в Spring достаточно фундаментальная вещь, которая по-сути описывает приложение.


Вариантов решения есть несколько, зависит от задачи:


  1. Создать базовый класс для тестов с одинаковой конфигурацией. Обычно, это означает тесты для одного и того же компонента (когда замоканы boundaries, об этом еще буду говорить в следующих статьях).
  2. Как уже отмечено — иметь одну базу для всех тестов, если пересоздавать контексты совершенно необходимо.
  3. Подумать над каким-то runtime созданием бинов, но это уже немного воевать с фреймворком.

Я скорее имел в виду, что библиотеки, вроде указанной, не делают ничего магического внутри.


А "реализовать интерфейс" и "добавить аннотацию" это для меня знание инструмента (Спринга). Чтобы им эффективно пользоваться — да, его надо знать. Но зато можно получать преимущества в виде продуктивности после.


В условном Го просто другой подход: вместо абстракций — повторения. Не лучше и не хуже — каждый выберет свой. В общем-то, в Спринг тоже никто не мешает игнорировать библиотеки тестирования — и хоть руками создавать контексты и делать все, что делает Spring Test, получив полный контроль над тестом. Просто потом захочется абстрагировать одну вещь, чтобы не повторять. Потому другую. А потом получится еще один фреймворк для тестирования.

Примечание переводчика: я запускал вот этот пример, прописав в зависимостях Spring Boot 2.2, и время запуска с ленивой инициализацией было 3 секунды, а без нее — 4. Думаю, что на более серьезных приложениях, существенного выигрыша во времени старта за счет использования ленивой инициализации мы не увидим.

В примере используется Hibernate в режиме ddl-auto=update. То есть, во время запуска приложения Hibernate подключается к базе, сканирует структуру таблиц, сканирует структуру Entities в приложении, находит разницу, вычисляет diff sql, обновляет структуру. После всего этого сколько запускается сам Spring, уже, в общем-то, не важно.


Hibernate вообще жадный до startup time, так что уж если хочется оптимизировать именно время запуска, то надо ставить и spring.data.jpa.repositories.bootstrap-mode=lazy (документация).

Рано сдаваться :)


Первая ошибка странная — это от Maven, а Intellij должна запускать приложение напрямую.


А вторая совсем странно, может установка Maven сломана? Что выдает ./mvnw --version и java -version? Под Windows, кстати, нужно запускать mvnw.cmd.

Да, убедили, полезная вещь чтобы упомянуть. Добавлю в статью, спасибо!

Не запустит, но и не должен, это не его обязанность. Вообще немного лукаво называть embedded-database-spring-test embedded — это самый обычный сервер Postgres (точнее, некий lightweight bundles of PostgreSQL binaries with reduced size), библиотека его просто запускает. Почему бы тогда не иметь локальный сервер Postgres и в нем создавать тестовую базу — не совсем понятно. Ну и если уж совсем-совсем надо запустить Postgres и TestContainers не подходят, то лучше использовать проверенные и поддерживаемые библиотеки, вроде https://github.com/yandex-qatools/postgresql-embedded.

Да, отличное замечание. Стандартные методы CrudRepository возвращают Iterable и я решил вернуть его же, хотя причины на это нет — Spring Data JPA может вернуть и Stream, и даже Set. Обновлю статью и код.

1
23 ...

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity