Java-70 Rename modules in Spring Security modules

This commit is contained in:
mikr
2020-08-15 20:25:37 +02:00
parent 4efc6f0f68
commit a4b01b4b09
60 changed files with 3 additions and 147 deletions

View File

@@ -0,0 +1,8 @@
## Spring Security Single Sign On
This module contains modules about single-sign-on with Spring Security
### Relevant Articles:
- [Spring Security Kerberos Integration](https://www.baeldung.com/spring-security-kerberos-integration)
- [Simple Single Sign-On with Spring Security OAuth2 (legacy stack)](https://www.baeldung.com/sso-spring-security-oauth2-legacy)

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung</groupId>
<artifactId>spring-security-oauth2-sso</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>spring-security-oauth2-sso</name>
<packaging>pom</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-boot-2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../../parent-boot-2</relativePath>
</parent>
<modules>
<module>spring-security-sso-auth-server</module>
<module>spring-security-sso-ui</module>
<module>spring-security-sso-ui-2</module>
<module>spring-security-sso-kerberos</module>
</modules>
<properties>
<rest-assured.version>3.1.0</rest-assured.version>
<oauth.version>2.3.3.RELEASE</oauth.version>
<oauth-auto.version>2.1.1.RELEASE</oauth-auto.version>
<spring-security-kerberos.version>1.0.1.RELEASE</spring-security-kerberos.version>
<apacheds-jdbm1.version>2.0.0-M2</apacheds-jdbm1.version>
</properties>
</project>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-security-sso-auth-server</artifactId>
<name>spring-security-sso-auth-server</name>
<packaging>war</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>spring-security-sso</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${oauth.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,39 @@
package com.baeldung.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("SampleClientId")
.secret(passwordEncoder.encode("secret"))
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true)
.redirectUris("http://localhost:8082/ui/login","http://localhost:8083/ui2/login","http://localhost:8082/login","http://www.example.com/")
// .accessTokenValiditySeconds(3600)
; // 1 hour
}
}

View File

@@ -0,0 +1,16 @@
package com.baeldung.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@SpringBootApplication
@EnableResourceServer
public class AuthorizationServerApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServerApplication.class, args);
}
}

View File

@@ -0,0 +1,41 @@
package com.baeldung.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception { // @formatter:off
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
.and().csrf().disable();
} // @formatter:on
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { // @formatter:off
auth.inMemoryAuthentication()
.withUser("john")
.password(passwordEncoder().encode("123"))
.roles("USER");
} // @formatter:on
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,16 @@
package com.baeldung.config;
import java.security.Principal;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
@RequestMapping("/user/me")
public Principal user(Principal principal) {
System.out.println(principal);
return principal;
}
}

View File

@@ -0,0 +1,3 @@
server.port=8081
server.servlet.context-path=/auth
#logging.level.org.springframework=DEBUG

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,18 @@
package com.baeldung;
import com.baeldung.config.AuthorizationServerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AuthorizationServerApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringContextTest {
@Test
public void whenLoadApplication_thenSuccess() {
}
}

View File

@@ -0,0 +1,51 @@
package com.baeldung;
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import io.restassured.RestAssured;
import io.restassured.response.Response;
public class UserInfoEndpointLiveTest {
@Test
public void givenAccessToken_whenAccessUserInfoEndpoint_thenSuccess() {
String accessToken = obtainAccessTokenUsingAuthorizationCodeFlow("john","123");
Response response = RestAssured.given().header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken).get("http://localhost:8081/auth/user/me");
assertEquals(HttpStatus.OK.value(), response.getStatusCode());
assertEquals("john", response.jsonPath().get("name"));
}
private String obtainAccessTokenUsingAuthorizationCodeFlow(String username, String password) {
final String authServerUri = "http://localhost:8081/auth";
final String redirectUrl = "http://www.example.com/";
final String authorizeUrl = authServerUri + "/oauth/authorize?response_type=code&client_id=SampleClientId&redirect_uri=" + redirectUrl;
final String tokenUrl = authServerUri + "/oauth/token";
// user login
Response response = RestAssured.given().formParams("username", username, "password", password).post(authServerUri + "/login");
final String cookieValue = response.getCookie("JSESSIONID");
// get authorization code
RestAssured.given().cookie("JSESSIONID", cookieValue).get(authorizeUrl);
response = RestAssured.given().cookie("JSESSIONID", cookieValue).post(authorizeUrl);
assertEquals(HttpStatus.FOUND.value(), response.getStatusCode());
final String location = response.getHeader(HttpHeaders.LOCATION);
final String code = location.substring(location.indexOf("code=") + 5);
// get access token
Map<String, String> params = new HashMap<String, String>();
params.put("grant_type", "authorization_code");
params.put("code", code);
params.put("client_id", "SampleClientId");
params.put("redirect_uri", redirectUrl);
response = RestAssured.given().auth().basic("SampleClientId", "secret").formParams(params).post(tokenUrl);
return response.jsonPath().getString("access_token");
}
}

View File

@@ -0,0 +1,2 @@
krb-test-workdir/
/bin/

View File

@@ -0,0 +1,4 @@
## Relevant articles:
- [Spring Security Kerberos Integration With MiniKdc](https://www.baeldung.com/spring-security-kerberos-integration)
- [Introduction to SPNEGO/Kerberos Authentication in Spring](https://www.baeldung.com/spring-security-kerberos)

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-security-sso-kerberos</artifactId>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>spring-security-sso</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-web</artifactId>
<version>${spring-security-kerberos.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-client</artifactId>
<version>${spring-security-kerberos.version}</version>
</dependency>
<dependency>
<groupId>org.apache.directory.jdbm</groupId>
<artifactId>apacheds-jdbm1</artifactId>
<version>${apacheds-jdbm1.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-test</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.directory.jdbm</groupId>
<artifactId>apacheds-jdbm1</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
<version>${spring-security-kerberos.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,13 @@
package com.baeldung.intro;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,88 @@
package com.baeldung.intro.config;
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.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;
import com.baeldung.intro.security.DummyUserDetailsService;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()), BasicAuthenticationFilter.class);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@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("HTTP/demo.kerberos.bealdung.com@baeldung.com");
ticketValidator.setKeyTabLocation(new FileSystemResource("baeldung.keytab"));
ticketValidator.setDebug(true);
return ticketValidator;
}
@Bean
public DummyUserDetailsService dummyUserDetailsService() {
return new DummyUserDetailsService();
}
}

View File

@@ -0,0 +1,16 @@
package com.baeldung.intro.security;
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"));
}
}

View File

@@ -0,0 +1,22 @@
package kerberos.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.nio.file.Paths;
@SpringBootApplication
class KerberosClientApp {
static {
System.setProperty("java.security.krb5.conf",
Paths.get(".\\krb-test-workdir\\krb5.conf").normalize().toAbsolutePath().toString());
System.setProperty("sun.security.krb5.debug", "true");
// disable usage of local kerberos ticket cache
System.setProperty("http.use.global.creds", "false");
}
public static void main(String[] args) {
SpringApplication.run(KerberosClientApp.class, args);
}
}

View File

@@ -0,0 +1,26 @@
package kerberos.client;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
class SampleClient {
@Value("${app.access-url}")
private String endpoint;
private RestTemplate restTemplate;
public SampleClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
void setRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
String getData() {
return restTemplate.getForObject(endpoint, String.class);
}
}

View File

@@ -0,0 +1,10 @@
package kerberos.client.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import(KerberosConfig.class)
class AppConfig {
}

View File

@@ -0,0 +1,22 @@
package kerberos.client.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.kerberos.client.KerberosRestTemplate;
import org.springframework.web.client.RestTemplate;
@Configuration
class KerberosConfig {
@Value("${app.user-principal}")
private String principal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Bean
public RestTemplate restTemplate() {
return new KerberosRestTemplate(keytabLocation, principal);
}
}

View File

@@ -0,0 +1,35 @@
package kerberos.kdc;
import org.apache.commons.io.FileUtils;
import org.springframework.security.kerberos.test.MiniKdc;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
class KerberosMiniKdc {
private static final String KRB_WORK_DIR = ".\\spring-security-sso\\spring-security-sso-kerberos\\krb-test-workdir";
public static void main(String[] args) throws Exception {
String[] config = MiniKdcConfigBuilder.builder()
.workDir(prepareWorkDir())
.confDir("minikdc-krb5.conf")
.keytabName("example.keytab")
.principals("client/localhost", "HTTP/localhost")
.build();
MiniKdc.main(config);
}
private static String prepareWorkDir() throws IOException {
Path dir = Paths.get(KRB_WORK_DIR);
File directory = dir.normalize().toFile();
FileUtils.deleteQuietly(directory);
FileUtils.forceMkdir(directory);
return dir.toString();
}
}

View File

@@ -0,0 +1,64 @@
package kerberos.kdc;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
class MiniKdcConfigBuilder {
private String workDir;
private String confDir;
private String keytabName;
private Collection<String> principals;
private MiniKdcConfigBuilder() {
// desired
}
static MiniKdcConfigBuilder builder() {
return new MiniKdcConfigBuilder();
}
MiniKdcConfigBuilder workDir(String workDir) {
this.workDir = workDir;
return this;
}
MiniKdcConfigBuilder confDir(String cfg) {
try {
URL resource = Thread.currentThread().getContextClassLoader().getResource(cfg);
URI uri = Objects.requireNonNull(resource).toURI();
this.confDir = Paths.get(uri).toString();
} catch (URISyntaxException cause) {
throw new IllegalStateException("Could not resolve path for: " + cfg, cause);
}
return this;
}
MiniKdcConfigBuilder keytabName(String keytabName) {
this.keytabName = Paths.get(workDir).resolve(keytabName).toString();
return this;
}
MiniKdcConfigBuilder principals(String... principals) {
this.principals = Arrays.asList(principals);
return this;
}
String[] build() {
Collection<String> miniKdcConfig = new ArrayList<>();
miniKdcConfig.add(workDir);
miniKdcConfig.add(confDir);
miniKdcConfig.add(keytabName);
miniKdcConfig.addAll(principals);
return miniKdcConfig.toArray(new String[0]);
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}

View File

@@ -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();
}
}

View File

@@ -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";
}
}

View File

@@ -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"));
}
}

View File

@@ -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

View File

@@ -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}
'}'

View File

@@ -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

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
>
<head>
<title>Spring Security Kerberos Example</title>
</head>
<body>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
</body>
</html>

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Security Kerberos Example</title>
</head>
<body>
<h1>Welcome!</h1>
<p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
</body>
</html>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
>
<head>
<title>Spring Security Kerberos Example</title>
</head>
<body>
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>

View File

@@ -0,0 +1,39 @@
package kerberos.client;
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.assertThrows;
/**
* Procedure to run this manual test:
* <ol>
* <li>Start {@code KerberosMiniKdc}</li>
* <li>Start {@code KerberizedServerApp}</li>
* <li>Run the test</li>
* </ol>
*/
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SampleClientManualTest {
@Autowired
private SampleClient sampleClient;
@Test
public void givenKerberizedRestTemplate_whenServiceCall_thenSuccess() {
assertEquals("data from kerberized server", sampleClient.getData());
}
@Test
public void givenRestTemplate_whenServiceCall_thenFail() {
sampleClient.setRestTemplate(new RestTemplate());
assertThrows(RestClientException.class, sampleClient::getData);
}
}

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-security-sso-ui-2</artifactId>
<name>spring-security-sso-ui-2</name>
<packaging>war</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>spring-security-sso</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>${oauth-auto.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,21 @@
package com.baeldung.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.RequestContextListener;
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {
@Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
}

View File

@@ -0,0 +1,23 @@
package com.baeldung.config;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableOAuth2Sso
@Configuration
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}

View File

@@ -0,0 +1,36 @@
package com.baeldung.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
@EnableWebMvc
public class UiWebConfig implements WebMvcConfigurer {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Override
public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
registry.addViewController("/")
.setViewName("forward:/index");
registry.addViewController("/index");
registry.addViewController("/securedPage");
}
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}

View File

@@ -0,0 +1,21 @@
server:
port: 8083
servlet:
context-path: /ui2
session:
cookie:
name: UI2SESSION
security:
basic:
enabled: false
oauth2:
client:
clientId: SampleClientId
clientSecret: secret
accessTokenUri: http://localhost:8081/auth/oauth/token
userAuthorizationUri: http://localhost:8081/auth/oauth/authorize
resource:
userInfoUri: http://localhost:8081/auth/user/me
spring:
thymeleaf:
cache: false

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spring Security SSO Client 2</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<div class="col-sm-12">
<h1>Spring Security SSO Client 2</h1>
<a class="btn btn-primary" href="securedPage">Login</a>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spring Security SSO Client 2</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<div class="col-sm-12">
<h1>Secured Page</h1>
Welcome, <span th:text="${#authentication.name}">Name</span>
<br/>
Your authorities are <span th:text="${#authentication.authorities}">authorities</span>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,18 @@
package com.baeldung;
import com.baeldung.config.UiApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = UiApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringContextTest {
@Test
public void whenLoadApplication_thenSuccess() {
}
}

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-security-sso-ui</artifactId>
<name>spring-security-sso-ui</name>
<packaging>war</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>spring-security-sso</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>${oauth-auto.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,21 @@
package com.baeldung.config;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.RequestContextListener;
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {
@Bean
public RequestContextListener requestContextListener() {
return new RequestContextListener();
}
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
}

View File

@@ -0,0 +1,23 @@
package com.baeldung.config;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableOAuth2Sso
@Configuration
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}

View File

@@ -0,0 +1,36 @@
package com.baeldung.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
@EnableWebMvc
public class UiWebConfig implements WebMvcConfigurer {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
@Override
public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
registry.addViewController("/")
.setViewName("forward:/index");
registry.addViewController("/index");
registry.addViewController("/securedPage");
}
@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
}

View File

@@ -0,0 +1,21 @@
server:
port: 8082
servlet:
context-path: /ui
session:
cookie:
name: UISESSION
security:
basic:
enabled: false
oauth2:
client:
clientId: SampleClientId
clientSecret: secret
accessTokenUri: http://localhost:8081/auth/oauth/token
userAuthorizationUri: http://localhost:8081/auth/oauth/authorize
resource:
userInfoUri: http://localhost:8081/auth/user/me
spring:
thymeleaf:
cache: false

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spring Security SSO</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<div class="col-sm-12">
<h1>Spring Security SSO</h1>
<a class="btn btn-primary" href="securedPage">Login</a>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Spring Security SSO</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
</head>
<body>
<div class="container">
<div class="col-sm-12">
<h1>Secured Page</h1>
Welcome, <span th:text="${#authentication.name}">Name</span>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,18 @@
package com.baeldung;
import com.baeldung.config.UiApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = UiApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class SpringContextTest {
@Test
public void whenLoadApplication_thenSuccess() {
}
}