Автор оригинала: Anshul Bansal.
1. Обзор
В этом уроке мы рассмотрим Spring Security SAML с Okta в качестве поставщика удостоверений личности (IdP) .
2. Что Такое SAML?
Язык разметки утверждений безопасности (SAML) – это открытый стандарт, который позволяет IdP безопасно отправлять данные аутентификации и авторизации пользователя поставщику услуг (SP) . Он использует сообщения на основе XML для связи между IdP и SP.
Другими словами, когда пользователь пытается получить доступ к службе, он должен войти в систему с помощью IdP. После входа в систему IdP отправляет атрибуты SAML с данными авторизации и аутентификации в формате XML в SP.
Помимо обеспечения защищенного механизма передачи аутентификации, SAML также поддерживает единый вход (SSO) , позволяя пользователям входить в систему один раз и повторно использовать те же учетные данные для входа в другие поставщики услуг.
3. Настройка Okta SAML
Во-первых, в качестве предварительного условия мы должны создать учетную запись разработчика Okta .
3.1. Создайте Новое Приложение
Затем мы создадим новую интеграцию веб-приложений с поддержкой SAML 2.0:
Затем мы заполним общую информацию, такую как название приложения и логотип приложения:
3.2. Редактирование интеграции SAML
На этом шаге мы предоставим параметры SAML, такие как URL-адрес единого входа и URI аудитории:
Наконец, мы можем предоставить обратную связь о нашей интеграции:
3.3. Просмотр Инструкций по настройке
После завершения мы можем просмотреть инструкции по настройке нашего приложения Spring Boot:
Примечание: мы должны скопировать инструкции, такие как URL-адрес эмитента IdP и XML метаданных IdP, которые потребуются в дальнейшем в конфигурациях безопасности Spring:
4. Установка пружинной загрузки
Помимо обычных зависимостей Maven, таких как spring-boot-starter-web и spring-boot-starter-security , нам потребуется spring-security-saml2-core зависимость:
org.springframework.boot spring-boot-starter-web 2.4.2 org.springframework.boot spring-boot-starter-security 2.4.2 org.springframework.security.extensions spring-security-saml2-core 1.0.10.RELEASE
Кроме того, не забудьте добавить Shibboleth репозиторий для загрузки последней версии opensaml jar , необходимой для spring-security-saml2-core зависимости:
Shibboleth Shibboleth https://build.shibboleth.net/nexus/content/repositories/releases/
В качестве альтернативы мы можем настроить зависимости в проекте Gradle:
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: "2.4.2" compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: "2.4.2" compile group: 'org.springframework.security.extensions', name: 'spring-security-saml2-core', version: "1.0.10.RELEASE"
5. Конфигурация безопасности Пружины
Теперь, когда у нас есть готовая настройка Okta SAML и проект Spring Boot, давайте начнем с конфигураций безопасности Spring, необходимых для интеграции SAML 2.0 с Okta.
5.1. Точка входа SAML
Во-первых, мы создадим компонент класса SAMLEntryPoint , который будет работать в качестве точки входа для аутентификации SAML:
@Bean public WebSSOProfileOptions defaultWebSSOProfileOptions() { WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions(); webSSOProfileOptions.setIncludeScoping(false); return webSSOProfileOptions; } @Bean public SAMLEntryPoint samlEntryPoint() { SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint(); samlEntryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions()); return samlEntryPoint; }
Здесь компонент WebSSOProfileOptions позволяет нам настроить параметры запроса, отправленного от SP к IdP с просьбой об аутентификации пользователя.
5.2. Вход и выход из системы
Далее, давайте создадим несколько фильтров для наших ОБРАЗЦОВ URI, таких как/ discovery, //login и/|/logout :
@Bean public FilterChainProxy samlFilter() throws Exception { Listchains = new ArrayList<>(); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"), samlWebSSOProcessingFilter())); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"), samlDiscovery())); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"), samlEntryPoint)); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"), samlLogoutFilter)); chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"), samlLogoutProcessingFilter)); return new FilterChainProxy(chains); }
Затем мы добавим несколько соответствующих фильтров и обработчиков:
@Bean public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception { SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter(); samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager()); samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler()); samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler()); return samlWebSSOProcessingFilter; } @Bean public SAMLDiscovery samlDiscovery() { SAMLDiscovery idpDiscovery = new SAMLDiscovery(); return idpDiscovery; } @Bean public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() { SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler = new SavedRequestAwareAuthenticationSuccessHandler(); successRedirectHandler.setDefaultTargetUrl("/home"); return successRedirectHandler; } @Bean public SimpleUrlAuthenticationFailureHandler authenticationFailureHandler() { SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler(); failureHandler.setUseForward(true); failureHandler.setDefaultFailureUrl("/error"); return failureHandler; }
До сих пор мы настроили точку входа для аутентификации ( samlEntryPoint ) и несколько цепочек фильтров. Итак, давайте глубоко погрузимся в их детали.
Когда пользователь пытается войти в систему в первый раз, samlEntryPoint обработает запрос на ввод. Затем компонент saml Discovery bean (если он включен) обнаружит IDP, с которым необходимо связаться для аутентификации.
Затем, когда пользователь входит в систему, IdP перенаправляет ответ SAML на /saml/sso URI для обработки , и соответствующий samlWebSSOProcessingFilter аутентифицирует соответствующий токен аутентификации.
В случае успеха successRedirectHandler перенаправит пользователя на целевой URL-адрес по умолчанию ( /home ). В противном случае authenticationFailureHandler перенаправит пользователя на URL-адрес /error .
Наконец, давайте добавим обработчики выхода для одиночных и глобальных выходов:
@Bean public SimpleUrlLogoutSuccessHandler successLogoutHandler() { SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler(); successLogoutHandler.setDefaultTargetUrl("/"); return successLogoutHandler; } @Bean public SecurityContextLogoutHandler logoutHandler() { SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); logoutHandler.setInvalidateHttpSession(true); logoutHandler.setClearAuthentication(true); return logoutHandler; } @Bean public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() { return new SAMLLogoutProcessingFilter(successLogoutHandler(), logoutHandler()); } @Bean public SAMLLogoutFilter samlLogoutFilter() { return new SAMLLogoutFilter(successLogoutHandler(), new LogoutHandler[] { logoutHandler() }, new LogoutHandler[] { logoutHandler() }); }
5.3. Обработка метаданных
Теперь мы предоставим XML метаданных IdP для SP. Это поможет вашему провайдеру узнать, на какую конечную точку SP он должен перенаправить, как только пользователь войдет в систему.
Итак, мы настроим генератор метаданных bean, чтобы позволить Spring SAML обрабатывать метаданные:
public MetadataGenerator metadataGenerator() { MetadataGenerator metadataGenerator = new MetadataGenerator(); metadataGenerator.setEntityId(samlAudience); metadataGenerator.setExtendedMetadata(extendedMetadata()); metadataGenerator.setIncludeDiscoveryExtension(false); metadataGenerator.setKeyManager(keyManager()); return metadataGenerator; } @Bean public MetadataGeneratorFilter metadataGeneratorFilter() { return new MetadataGeneratorFilter(metadataGenerator()); } @Bean public ExtendedMetadata extendedMetadata() { ExtendedMetadata extendedMetadata = new ExtendedMetadata(); extendedMetadata.setIdpDiscoveryEnabled(false); return extendedMetadata; }
Для Генератора метаданных bean требуется экземпляр Менеджера ключей для шифрования обмена между SP и IdP:
@Bean public KeyManager keyManager() { DefaultResourceLoader loader = new DefaultResourceLoader(); Resource storeFile = loader.getResource(samlKeystoreLocation); Mappasswords = new HashMap<>(); passwords.put(samlKeystoreAlias, samlKeystorePassword); return new JKSKeyManager(storeFile, samlKeystorePassword, passwords, samlKeystoreAlias); }
Здесь мы должны создать и предоставить хранилище ключей для компонента KeyManager . Мы можем создать самозаверяющий ключ и хранилище ключей с помощью команды JRE:
keytool -genkeypair -alias baeldungspringsaml -keypass baeldungsamlokta -keystore saml-keystore.jks
5.4. Менеджер метаданных
Затем мы настроим метаданные IdP в нашем приложении Spring Boot с помощью экземпляра ExtendedMetadataDelegate :
@Bean @Qualifier("okta") public ExtendedMetadataDelegate oktaExtendedMetadataProvider() throws MetadataProviderException { File metadata = null; try { metadata = new File("./src/main/resources/saml/metadata/sso.xml"); } catch (Exception e) { e.printStackTrace(); } FilesystemMetadataProvider provider = new FilesystemMetadataProvider(metadata); provider.setParserPool(parserPool()); return new ExtendedMetadataDelegate(provider, extendedMetadata()); } @Bean @Qualifier("metadata") public CachingMetadataManager metadata() throws MetadataProviderException, ResourceException { Listproviders = new ArrayList<>(); providers.add(oktaExtendedMetadataProvider()); CachingMetadataManager metadataManager = new CachingMetadataManager(providers); metadataManager.setDefaultIDP(defaultIdp); return metadataManager; }
Здесь мы проанализировали метаданные из sso.xml файл, содержащий XML метаданных IdP, скопированный из учетной записи разработчика Okta при просмотре инструкций по установке.
Аналогично, переменная Idp по умолчанию содержит URL-адрес эмитента IdP, скопированный из учетной записи разработчика Okta.
5.5. Синтаксический анализ XML
Для синтаксического анализа XML мы можем использовать экземпляр класса StaticBasicParserPool :
@Bean(initMethod = "initialize") public StaticBasicParserPool parserPool() { return new StaticBasicParserPool(); } @Bean(name = "parserPoolHolder") public ParserPoolHolder parserPoolHolder() { return new ParserPoolHolder(); }
5.6. Процессор SAML
Затем мы требуем, чтобы процессор проанализировал сообщение SAML из HTTP-запроса:
@Bean public HTTPPostBinding httpPostBinding() { return new HTTPPostBinding(parserPool(), VelocityFactory.getEngine()); } @Bean public HTTPRedirectDeflateBinding httpRedirectDeflateBinding() { return new HTTPRedirectDeflateBinding(parserPool()); } @Bean public SAMLProcessorImpl processor() { ArrayListbindings = new ArrayList<>(); bindings.add(httpRedirectDeflateBinding()); bindings.add(httpPostBinding()); return new SAMLProcessorImpl(bindings); }
Здесь мы использовали привязки POST и Redirect в отношении нашей конфигурации в учетной записи разработчика Okta.
5.7. Реализация поставщика аутентификации SAML
Наконец, нам требуется пользовательская реализация класса SAML Authentication Provider для проверки экземпляра класса ExpiringUsernameAuthenticationToken и установки полученных полномочий:
public class CustomSAMLAuthenticationProvider extends SAMLAuthenticationProvider { @Override public Collection extends GrantedAuthority> getEntitlements(SAMLCredential credential, Object userDetail) { if (userDetail instanceof ExpiringUsernameAuthenticationToken) { Listauthorities = new ArrayList (); authorities.addAll(((ExpiringUsernameAuthenticationToken) userDetail).getAuthorities()); return authorities; } else { return Collections.emptyList(); } } }
Кроме того, мы должны настроить Пользовательский поставщик аутентификации SAML в качестве компонента в классе SecurityConfig :
@Bean public SAMLAuthenticationProvider samlAuthenticationProvider() { return new CustomSAMLAuthenticationProvider(); }
5.8. Конфигурация безопасности
Наконец, мы настроим базовую безопасность HTTP с помощью уже обсужденных samlEntryPoint и samlFilter :
@Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); http.httpBasic().authenticationEntryPoint(samlEntryPoint); http .addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class) .addFilterAfter(samlFilter(), BasicAuthenticationFilter.class) .addFilterBefore(samlFilter(), CsrfFilter.class); http .authorizeRequests() .antMatchers("/").permitAll() .anyRequest().authenticated(); http .logout() .addLogoutHandler((request, response, authentication) -> { response.sendRedirect("/saml/logout"); }); }
Вуаля! Мы завершили настройку SAML Spring Security, которая позволяет пользователю войти в IdP, а затем получить данные аутентификации пользователя в формате XML от IdP. Наконец, он аутентифицирует токен пользователя, чтобы разрешить доступ к нашему веб-приложению.
6. Домашний контролер
Теперь, когда наши конфигурации SAML Spring Security готовы вместе с настройкой учетной записи разработчика Okta, мы можем настроить простой контроллер для обеспечения целевой страницы и домашней страницы .
6.1. Отображение индекса и аутентификации
Во-первых, давайте добавим сопоставления к целевому URL-адресу по умолчанию (/) и/|/auth URI:
@RequestMapping("/") public String index() { return "index"; } @GetMapping(value = "/auth") public String handleSamlAuth() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null) { return "redirect:/home"; } else { return "/"; } }
Затем мы добавим простой index.html это позволяет пользователю перенаправить аутентификацию Okta SAML с помощью ссылки login :
Baeldung Spring Security SAML Welcome to Baeldung Spring Security SAML
Login
Теперь мы готовы запустить наше приложение Spring Boot и получить к нему доступ по адресу http://localhost:8080/ :
Страница входа в систему Okta должна открываться при нажатии на Авторизоваться ссылка:
6.2. Домашняя Страница
Затем давайте добавим сопоставление с URL-адресом /home , чтобы перенаправить пользователя при успешной аутентификации:
@RequestMapping("/home") public String home(Model model) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); model.addAttribute("username", authentication.getPrincipal()); return "home"; }
Кроме того, мы добавим home.html для отображения вошедшего пользователя и ссылки на выход из системы:
Baeldung Spring Security SAML: Home Welcome!
You are successfully logged in!You are logged as null.
Logout
После успешного входа в систему мы должны увидеть домашнюю страницу:
7. Заключение
В этом уроке мы обсудили интеграцию Spring Security SAML с Okta.
Во-первых, мы создали учетную запись разработчика Okta с веб-интеграцией SAML 2.0. Затем мы создали проект Spring Boot с необходимыми зависимостями Maven.
Затем мы выполнили все необходимые настройки для SAML Spring Security, такие как samlEntryPoint , ssmlfilter , обработка метаданных и процессор SAML .
Наконец, мы создали контроллер и несколько страниц, таких как index и home , чтобы проверить нашу интеграцию SAML с Okta.
Как обычно, исходный код доступен на GitHub .