Введение
Единый вход (SSO) — это процесс аутентификации, который позволяет пользователям получать доступ к нескольким приложениям, используя единый набор учетных данных для входа. Это повышает безопасность и удобство работы пользователей, упрощая управление несколькими учетными записями в различных службах. Эта статья проведет вас через процесс создания многопользовательской корпоративной SSO-интеграции с использованием Spring Boot, которая может интегрироваться с любым поставщиком SSO, таким как Google, Outlook и другими, для разных клиентов. Мы предоставим подробные пояснения и примеры кода для лучшего понимания процесса.
Предпосылки
Прежде чем мы углубимся, убедитесь, что у вас установлено и настроено следующее:
- Комплект для разработки Java (JDK) 8 или более поздней версии
- Апач Мавен
- Текстовый редактор или интегрированная среда разработки (IDE) по вашему выбору.
- Базовое понимание Spring Boot, OAuth 2.0 и OpenID Connect (OIDC)
Начало работы с Spring Boot
- Создайте новый проект Spring Boot с помощью Spring Initializr. Выберите следующие зависимости:
- Веб: «Весенняя паутина»
- Безопасность: «Весенняя безопасность»
- Клиент OAuth 2.0: «Клиент Spring Boot OAuth2»
2. Загрузите и извлеките сгенерированный проект.
3. Откройте проект в предпочтительной среде IDE и убедитесь, что все зависимости правильно загружены.
Внедрение SSO-интеграции
Мы будем использовать OAuth 2.0 и OpenID Connect (OIDC) для интеграции с внешними поставщиками SSO. OIDC — это уровень идентификации, созданный поверх OAuth 2.0, который обеспечивает функции аутентификации и авторизации. Выполните следующие действия, чтобы реализовать интеграцию SSO с несколькими арендаторами.
- Обновите файл
application.properties
:
# Base properties for SSO integration spring.security.oauth2.client.registration.dynamic.registration-id=<your_registration_id> spring.security.oauth2.client.registration.dynamic.scope=openid,email,profile # These properties will be dynamically overridden for each tenant spring.security.oauth2.client.registration.dynamic.client-id=<your_client_id> spring.security.oauth2.client.registration.dynamic.client-secret=<your_client_secret> spring.security.oauth2.client.registration.dynamic.provider=<your_sso_provider>
Замените <your_registration_id>
, <your_client_id>
, <your_client_secret>
и <your_sso_provider>
фактическими значениями, полученными от поставщика единого входа.
2. Создайте новый класс DynamicOAuth2ClientRegistration
для хранения сведений о динамической регистрации клиента:
package com.example.sso; import org.springframework.security.oauth2.client.registration.ClientRegistration; public class DynamicOAuth2ClientRegistration { private String tenantId; private ClientRegistration clientRegistration; // Constructor, getters, and setters }
3. Создайте новый класс DynamicOAuth2ClientRegistrationRepository
, реализующий ClientRegistrationRepository
:
package com.example.sso; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import java.util.HashMap; import java.util.Map; public class DynamicOAuth2ClientRegistrationRepository implements ClientRegistrationRepository { private final Map<String, DynamicOAuth2ClientRegistration> dynamicClientRegistrations; public DynamicOAuth2ClientRegistrationRepository() { this.dynamicClientRegistrations = new HashMap<>(); } @Override public ClientRegistration findByRegistrationId(String registrationId) { // Retrieve the client registration for the given tenant DynamicOAuth2ClientRegistration dynamicRegistration = dynamicClientRegistrations.get(registrationId); return dynamicRegistration == null ? null : dynamicRegistration.getClientRegistration(); } // Additional methods for managing dynamic client registrations }
4. Измените класс WebSecurityConfig
, чтобы настроить bean-компонент DynamicOAuth2ClientRegistrationRepository
и внедрить его в конфигурацию HttpSecurity
:
package com.example.sso; // ...other imports... import org.springframework.context.annotation.Bean; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public DynamicOAuth2ClientRegistrationRepository dynamicClientRegistrationRepository() { return new DynamicOAuth2ClientRegistrationRepository(); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // ...existing code... .and() .oauth2Login() .clientRegistrationRepository(dynamicClientRegistrationRepository()) .defaultSuccessURL("/user", true); } }
5. Создайте новый класс Tenant
для представления информации о каждом арендаторе:
package com.example.sso; public class Tenant { private String id; private String name; private String ssoProvider; private String clientId; private String clientSecret; // Constructor, getters, and setters }
6. Добавьте новый класс TenantRepository
для хранения информации об арендаторах и управления ею:
package com.example.sso; import java.util.List; public interface TenantRepository { Tenant findById(String id); List<Tenant> findAll(); Tenant save(Tenant tenant); void deleteById(String id); }
Вы можете реализовать TenantRepository
, используя предпочтительную технологию базы данных (например, JPA, JDBC, MongoDB и т. д.). В этой статье не рассматриваются конкретные детали реализации базы данных, но вы можете найти дополнительную информацию в Документации Spring Data.
7. Создайте новый класс TenantService
для обработки логики конкретного арендатора:
package com.example.sso; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @Service public class TenantService { @Autowired private TenantRepository tenantRepository; public Tenant findById(String id) { return tenantRepository.findById(id); } public List<Tenant> findAll() { return tenantRepository.findAll(); } public Tenant save(Tenant tenant) { return tenantRepository.save(tenant); } public void deleteById(String id) { tenantRepository.deleteById(id); } }
8. Обновите класс DynamicOAuth2ClientRegistrationRepository
, чтобы внедрить TenantService
и получить сведения о регистрации клиента для конкретного арендатора:
package com.example.sso; // ...other imports... import org.springframework.beans.factory.annotation.Autowired; public class DynamicOAuth2ClientRegistrationRepository implements ClientRegistrationRepository { // ...existing code... @Autowired private TenantService tenantService; @Override public ClientRegistration findByRegistrationId(String registrationId) { Tenant tenant = tenantService.findById(registrationId); if (tenant == null) { return null; } // Create a dynamic client registration based on the tenant's SSO provider DynamicOAuth2ClientRegistration dynamicRegistration = createDynamicClientRegistration(tenant); return dynamicRegistration == null ? null : dynamicRegistration.getClientRegistration(); } // ...other methods... private DynamicOAuth2ClientRegistration createDynamicClientRegistration(Tenant tenant) { // Retrieve the OAuth2 provider details based on the tenant's SSO provider // and create a dynamic client registration using the tenant's client ID and secret } }
9. Обновите класс WebSecurityConfig
, чтобы настроить bean-компонент DynamicOAuth2ClientRegistrationRepository
и внедрить его в конфигурацию HttpSecurity
:
package com.example.sso; // ...other imports... import org.springframework.context.annotation.Bean; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public DynamicOAuth2ClientRegistrationRepository dynamicClientRegistrationRepository() { return new DynamicOAuth2ClientRegistrationRepository(); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // ...existing code... .and() .oauth2Login() .clientRegistrationRepository(dynamicClientRegistrationRepository()) .defaultSuccessURL("/user", true); } }
Теперь ваше приложение готово к интеграции многопользовательской системы единого входа с различными поставщиками. Вы можете хранить поставщика единого входа для каждого арендатора, идентификатор клиента и секрет клиента в базе данных и динамически извлекать сведения о регистрации клиента на основе текущего арендатора.
Тестирование мультитенантной интеграции единого входа
Чтобы протестировать интеграцию SSO с несколькими арендаторами, вы можете создать простое веб-приложение со страницей входа, которая отображает доступных поставщиков SSO для каждого арендатора. Выполните следующие шаги:
- Обновите файл
application.properties
, чтобы включить зависимость Thymeleaf:
spring.thymeleaf.cache=false
2. Добавьте зависимость Thymeleaf к вашему pom.xml
:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
3. Создайте новый класс контроллера LoginController
:
package com.example.sso; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @Controller public class LoginController { @Autowired private TenantService tenantService; @GetMapping("/login/{tenantId}") public String login(@PathVariable("tenantId") String tenantId, Model model) { Tenant tenant = tenantService.findById(tenantId); if (tenant == null) { return "error"; } model.addAttribute("tenant", tenant); return "login"; } }
4. Создайте новый шаблон Thymeleaf src/main/resources/templates/login.html
:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Login</title> </head> <body> <h1>Login with SSO Provider</h1> <div th:if="${tenant.ssoProvider == 'google'}"> <a th:href="@{/oauth2/authorization/{tenantId}(tenantId=${tenant.id})}">Login with Google</a> </div> <div th:if="${tenant.ssoProvider == 'outlook'}"> <a th:href="@{/oauth2/authorization/{tenantId}(tenantId=${tenant.id})}">Login with Outlook</a> </div> <!-- Add other SSO providers as needed --> </body> </html>
Теперь вы можете запустить свое приложение и протестировать интеграцию SSO с несколькими арендаторами, посетив URL-адрес /login/{tenantId}
с соответствующим идентификатором арендатора. После успешной аутентификации пользователь будет перенаправлен на URL-адрес /user
.
Заключение
В этой статье мы продемонстрировали, как создать мультитенантную интеграцию SSO предприятия с Spring Boot, которая может интегрироваться с различными поставщиками SSO, такими как Google, Outlook, Okta и другими. Реализация использует OAuth 2.0 и OpenID Connect (OIDC) для обеспечения функций аутентификации и авторизации. Мы рассмотрели необходимую конфигурацию, дизайн базы данных.
🔗 Свяжитесь со мной в LinkedIn!
Я надеюсь, что вы нашли эту статью полезной! Если вы хотите узнать больше и быть в курсе моих последних идей и статей, не стесняйтесь связаться со мной в LinkedIn.
Давайте расширять наши сети, участвовать в содержательных дискуссиях и делиться своим опытом в мире разработки программного обеспечения и за его пределами. С нетерпением ждем связи с вами! 😊