miniKdcConfig = new ArrayList<>();
+
+ miniKdcConfig.add(workDir);
+ miniKdcConfig.add(confDir);
+ miniKdcConfig.add(keytabName);
+ miniKdcConfig.addAll(principals);
+
+ return miniKdcConfig.toArray(new String[0]);
+ }
+}
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/KerberizedServerApp.java b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/KerberizedServerApp.java
new file mode 100644
index 0000000000..8286013605
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/KerberizedServerApp.java
@@ -0,0 +1,22 @@
+package kerberos.server;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import java.nio.file.Paths;
+
+@SpringBootApplication
+public class KerberizedServerApp {
+
+ static {
+ System.setProperty("java.security.krb5.conf",
+ Paths.get(".\\spring-security-sso\\spring-security-sso-kerberos\\krb-test-workdir\\krb5.conf")
+ .normalize().toAbsolutePath().toString());
+ System.setProperty("sun.security.krb5.debug", "true");
+ }
+
+ public static void main(String[] args) {
+
+ SpringApplication.run(KerberizedServerApp.class, args);
+ }
+}
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/config/MvcConfig.java b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/config/MvcConfig.java
new file mode 100644
index 0000000000..3ad07e407b
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/config/MvcConfig.java
@@ -0,0 +1,18 @@
+package kerberos.server.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+class MvcConfig extends WebMvcConfigurerAdapter {
+
+ @Override
+ public void addViewControllers(ViewControllerRegistry registry) {
+ registry.addViewController("/home").setViewName("home");
+ registry.addViewController("/").setViewName("home");
+ registry.addViewController("/hello").setViewName("hello");
+ registry.addViewController("/login").setViewName("login");
+ }
+
+}
\ No newline at end of file
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/config/WebSecurityConfig.java b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/config/WebSecurityConfig.java
new file mode 100644
index 0000000000..5d241c5823
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/config/WebSecurityConfig.java
@@ -0,0 +1,103 @@
+package kerberos.server.config;
+
+import kerberos.server.service.DummyUserDetailsService;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;
+import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
+import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;
+import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
+import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;
+import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
+import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
+
+@Configuration
+@EnableWebSecurity
+class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Value("${app.service-principal}")
+ private String servicePrincipal;
+
+ @Value("${app.keytab-location}")
+ private String keytabLocation;
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http.exceptionHandling()
+ .authenticationEntryPoint(spnegoEntryPoint())
+ .and()
+ .authorizeRequests().antMatchers("/", "/home").permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin().loginPage("/login").permitAll()
+ .and()
+ .logout().permitAll()
+ .and()
+ .addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()),
+ BasicAuthenticationFilter.class);
+ }
+
+ @Bean
+ public AuthenticationManager anAuthenticationManager() throws Exception {
+ return authenticationManager();
+ }
+
+ @Override
+ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+ auth.authenticationProvider(kerberosAuthenticationProvider())
+ .authenticationProvider(kerberosServiceAuthenticationProvider());
+ }
+
+ @Bean
+ public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
+ KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
+ SunJaasKerberosClient client = new SunJaasKerberosClient();
+ client.setDebug(true);
+ provider.setKerberosClient(client);
+ provider.setUserDetailsService(dummyUserDetailsService());
+ return provider;
+ }
+
+ @Bean
+ public SpnegoEntryPoint spnegoEntryPoint() {
+ return new SpnegoEntryPoint("/login");
+ }
+
+ @Bean
+ public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
+ AuthenticationManager authenticationManager) {
+ SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
+ filter.setAuthenticationManager(authenticationManager);
+ return filter;
+ }
+
+ @Bean
+ public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
+ KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
+ provider.setTicketValidator(sunJaasKerberosTicketValidator());
+ provider.setUserDetailsService(dummyUserDetailsService());
+ return provider;
+ }
+
+ @Bean
+ public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
+ SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
+ ticketValidator.setServicePrincipal(servicePrincipal);
+ ticketValidator.setKeyTabLocation(new FileSystemResource(keytabLocation));
+ ticketValidator.setDebug(true);
+ return ticketValidator;
+ }
+
+ @Bean
+ public DummyUserDetailsService dummyUserDetailsService() {
+ return new DummyUserDetailsService();
+ }
+
+}
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/controller/SampleController.java b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/controller/SampleController.java
new file mode 100644
index 0000000000..b1d3e6fb90
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/controller/SampleController.java
@@ -0,0 +1,15 @@
+package kerberos.server.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/endpoint")
+class SampleController {
+
+ @GetMapping
+ String getIt() {
+ return "data from kerberized server";
+ }
+}
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/service/DummyUserDetailsService.java b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/service/DummyUserDetailsService.java
new file mode 100644
index 0000000000..06942d8dc7
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/java/kerberos/server/service/DummyUserDetailsService.java
@@ -0,0 +1,16 @@
+package kerberos.server.service;
+
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+public class DummyUserDetailsService implements UserDetailsService {
+
+ @Override
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ return new User(username, "notUsed", true, true, true, true, AuthorityUtils.createAuthorityList("ROLE_USER"));
+ }
+
+}
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/resources/application.properties b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/application.properties
new file mode 100644
index 0000000000..b36575460c
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/application.properties
@@ -0,0 +1,6 @@
+# make sure the same data is configured in KerberosMiniKdc
+# otherwise configuration/communication error will occur
+app.service-principal=HTTP/localhost
+app.user-principal=client/localhost
+app.keytab-location=@project.basedir@\\krb-test-workdir\\example.keytab
+app.access-url=http://localhost:8080/endpoint
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/resources/minikdc-krb5.conf b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/minikdc-krb5.conf
new file mode 100644
index 0000000000..ea1e9d1ceb
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/minikdc-krb5.conf
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+[libdefaults]
+default_realm = {0}
+udp_preference_limit = 1
+
+[realms]
+{0} = '{'
+ kdc = {1}:{2}
+ '}'
\ No newline at end of file
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/resources/minikdc.ldiff b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/minikdc.ldiff
new file mode 100644
index 0000000000..603ccb5fd9
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/minikdc.ldiff
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+dn: ou=users,dc=${0},dc=${1}
+objectClass: organizationalUnit
+objectClass: top
+ou: users
+
+dn: uid=krbtgt,ou=users,dc=${0},dc=${1}
+objectClass: top
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: krb5principal
+objectClass: krb5kdcentry
+cn: KDC Service
+sn: Service
+uid: krbtgt
+userPassword: secret
+krb5PrincipalName: krbtgt/${2}.${3}@${2}.${3}
+krb5KeyVersionNumber: 0
+
+dn: uid=ldap,ou=users,dc=${0},dc=${1}
+objectClass: top
+objectClass: person
+objectClass: inetOrgPerson
+objectClass: krb5principal
+objectClass: krb5kdcentry
+cn: LDAP
+sn: Service
+uid: ldap
+userPassword: secret
+krb5PrincipalName: ldap/${4}@${2}.${3}
+krb5KeyVersionNumber: 0
\ No newline at end of file
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/hello.html b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/hello.html
new file mode 100644
index 0000000000..71a756386b
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/hello.html
@@ -0,0 +1,10 @@
+
+
+
+ Spring Security Kerberos Example
+
+
+ Hello [[${#httpServletRequest.remoteUser}]]!
+
+
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/home.html b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/home.html
new file mode 100644
index 0000000000..d8e37e443d
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/home.html
@@ -0,0 +1,10 @@
+
+
+
+ Spring Security Kerberos Example
+
+
+ Welcome!
+ Click here to see a greeting.
+
+
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/login.html b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/login.html
new file mode 100644
index 0000000000..b96252192f
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/main/resources/templates/login.html
@@ -0,0 +1,20 @@
+
+
+
+ Spring Security Kerberos Example
+
+
+
+ Invalid username and password.
+
+
+ You have been logged out.
+
+
+
+
diff --git a/spring-security-sso/spring-security-sso-kerberos/src/test/java/kerberos/client/SampleServiceManualTest.java b/spring-security-sso/spring-security-sso-kerberos/src/test/java/kerberos/client/SampleServiceManualTest.java
new file mode 100644
index 0000000000..d0d9f0ae4b
--- /dev/null
+++ b/spring-security-sso/spring-security-sso-kerberos/src/test/java/kerberos/client/SampleServiceManualTest.java
@@ -0,0 +1,43 @@
+package kerberos.client;
+
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+ * Procedure to run this manual test:
+ *
+ * - Start {@code KerberosMiniKdc}
+ * - Start {@code KerberizedServerApp}
+ * - Run the test
+ *
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@FixMethodOrder
+public class SampleServiceManualTest {
+
+ @Autowired
+ private SampleService sampleService;
+
+ @Test
+ public void a_givenKerberizedRestTemplate_whenServiceCall_thenSuccess() {
+ assertNotNull(sampleService);
+ assertEquals("data from kerberized server", sampleService.getData());
+ }
+
+ @Test
+ public void b_givenRestTemplate_whenServiceCall_thenFail() {
+ sampleService.setRestTemplate(new RestTemplate());
+ assertThrows(RestClientException.class, sampleService::getData);
+ }
+}
\ No newline at end of file