Comments 9
Получается Вы нарушаете безопасность своего защищенного приложения подключением к неконтролируемому с Вашей стороны сервису Bitbucket ради того, чтобы только он знал о логин-паролях к Вашему приложению. Бред какой-то…
Я сам достаточно глубоко в этом, довел данную связку почти до «из коробки», поэтому могу сказать, что Spring Security последних версий может намного больше. Авторизация через внешние ресурсы — это очень спорная штука:
а) доступность — гарантия, что завтра это будет работать? сервер вообще, линки, структура;
б) безопасность — да, ресурс, которому верят, но ...;
в) задержки — для безопасности expire_on ставится небольшой, значит, надо относительно часто бегать за токеном, с удаленным ресурсом с нагрузкой возможны задержки;
г) доступность — как в предыдущем, только есть гарантия, что ресурс не имеет ограничений на запросы (с IP, в минуту и т.д.)? — риск.
Spring Security на борту уже имеет AuthorizationServer и ResourceServer, а также OAuth2Client. Сейчас достаточно модно (и удобно) создавать мультисервисную архитектуру проекта. Так почему не сделать один модуль — авторизационный OAuth2 сервер? Нет всех ограничений, описанных выше, плюс полный контроль. Как бонус — разделение БД на доступную для сервисов-модулей с данными проекта и отдельно авторизационную с монопольным доступом из авторизационного модуля. Еще бонус — не примитивный токет, а OAuth2 token + JWT + OpenID, это значительно увеличивает удобство пользования такой авторизацией модулями.
Кроме этого, грамотная реализация OAuth2 авторизации включает в себя не только access token, но и refresh token, для оптимизации запросов к авторизационному модулю/серверу.
Также на практике более гибкая логика модуля, где применяются scopes, а не authorities — это позволяет очень гибко обеспечивать секьюрность методов и классов. То есть у customers назначены authorities, покрывающие определенные наборы scopes, а методы классов в большинстве своем защищены определением разрешенных scopes, типа
@PreAuthorize("#oauth2.hasScope('mod:some_scope')")
Кстати, с такой реализацией не только пользователи имеют авторизацию, но и подключаемые модули, у всех свои clientId и clientSecret. Плюс использование OAuth2RestTemplate, что делает еще и сами запросы к серверу секьюрными.
Спасибо за работу над статьей, особенно полезны для новичков будут детали протокола OAuth2. Но и здесь есть важный момент — сам по себе OAuth2 это не протокол аутентификации, это протокол авторизации. А вот OpenID Connect это да, аутентификация, и Spring Security его отлично поддерживает.
Но все, что касается Spring Security в статье это, скорее bad practices, так делать не надо.
- Для начала, стоит указать версию SS — поддержка OAuth2 в 5-й версии внесена в ядро и сильно отличается от 4-й.
- Если используется Spring Boot, то руками создавать
ClientRegistration
не нужно, SB это сделает за вас. Более того, Boot из коробки поможет сконфигурировать стандартные OpenID провайдеры вроде Facebook, Google, Github, даже Token URI не надо указывать. - Ну и самое главное — своя реализация UserService не просто не нужна, а даже вредна — например, тут не проверяются многие пункты стандарта, не надо так делать в реальных проектах. Вся конфигурация, в принципе, делается так:
.oauth2Login()
. Все, SS создаст стандартный UserService для работы с OpenID endpoint (/userinfo
), если провайдер поддерживает, провалидирует ответ, создаст IdToken и вообще сделает все правильно и хорошо.
- В реализации используется 5-я версия и она действительно отличается от 4-й, хотя в целом конфигурирование приведенных вещей очень похоже и там и там.
- Согласен, в данном случае создавать его руками было не обязательно. Хотя Bitbucket не является стандартным провайдером, поэтому все тоже самое пришлось бы сделать в application.properties. Вариант с application.properties не подойдет, если нужно динамически создавать ClientRegistration в зависимости от каких-либо условий. В данном случае пример был вырезан из более крупного приложения и переносить это в application.properties руки не дошли.
- Насчет своей реализации UserService можно и согласиться и нет. Поддерживаю, что у этого решения есть свои минусы, но это решение использует стандартное API SS, поэтому тоже имеет право на существование.
А как быть, если ВКонтакте на запрос юзера (по user-info-uri) не Map возвращает, а сложную структуру c массивом из одного элемента — который и есть авторизовавшийся юзер:
response: {
[
{
id: "123",
first_name: "имя"
...
}
]
}
}
DefaultOAuth2UserService, как я понимаю, только простейший объект без вложенности парсит.
Пробую создать VkOAuth2User implements OAuth2User и добавить в конфигурацию, как ниже. Это правильное направление, или куда копать?
http.oauth2Login(oauth2Login ->
oauth2Login
.userInfoEndpoint(userInfoEndpoint ->
userInfoEndpoint
.customUserType(VkOAuth2User.class, "vk")
)
Я, кажется, имел в виду что вредна конкретно в этом случае. Своя реализация UserDetailsService нужна как раз чтобы поддержать такие вот сложные и нестандартные ответы от UserInfo Endpoint. Ответ от ВК выглядит странно, такое ощущение что он не следует спецификации. Тогда да, придется писать свой сервис.
.customUSerType
уже deprecated, и рекомендуется использовать свой сервис и делегацию.
http.oauth2Login().tokenEndpoint().accessTokenResponseClient(accessTokenResponseClient());
Spring Security — пример веб приложения с авторизацией по протоколу OAuth2 через BitBucket