Skip to content

Instantly share code, notes, and snippets.

@thomasdarimont
Created November 23, 2016 22:31
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save thomasdarimont/06c1e0d3ebefed78ae3d29f92c8c285e to your computer and use it in GitHub Desktop.
Save thomasdarimont/06c1e0d3ebefed78ae3d29f92c8c285e to your computer and use it in GitHub Desktop.
Example for using the keycloak Spring Security Adapter (2.4.0.Final) with a Bearer-only Spring Boot App deployed in JBoss EAP
package de.tdlabs.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
@SpringBootApplication
public class App extends SpringBootServletInitializer {
/**
* Initializes this application when running as a standalone application.
*/
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
/**
* Initializes this application when running in a servlet container (e.g. Tomcat)
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(App.class);
}
}
package de.tdlabs.examples;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/customers")
//@CacheControl(policy = CachePolicy.NO_CACHE)
public class CustomerApiController {
private static final Logger log = LoggerFactory.getLogger(CustomerApiController.class);
@RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public List<String> getCustomers() {
log.info("Returning customer list.");
return Arrays.asList("Scott Rossillo", "Kyung Lee", "Keith Leggins", "Ben Loy");
}
}

Query /customers endpoint on database service without any authentication

$ curl -v http://localhost:8080/keycloak-example-spring-security-war-0.0.1.BUILD-SNAPSHOT/customers
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /keycloak-example-spring-security-war-0.0.1.BUILD-SNAPSHOT/customers HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.46.0
> Accept: */*
> 
< HTTP/1.1 401 Unauthorized
< Expires: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< X-Powered-By: Undertow/1
< Server: JBoss-EAP/7
< X-XSS-Protection: 1; mode=block
< Pragma: no-cache
< X-Frame-Options: DENY
< Date: Wed, 23 Nov 2016 22:25:33 GMT
< Connection: keep-alive
< WWW-Authenticate: Bearer realm="Unknown"
< X-Content-Type-Options: nosniff
< Transfer-Encoding: chunked
< Content-Type: application/json;charset=UTF-8
< 
* Connection #0 to host localhost left intact
{"timestamp":1479939933320,"status":401,"error":"Unauthorized","message":"Unauthorized","path":"/keycloak-example-spring-security-war-0.0.1.BUILD-SNAPSHOT/customers"}%  

Retrieve access token via curl from a dummy confidential client

KC_REALM=spring-security-adapter-test
KC_USERNAME=tester
KC_PASSWORD=test
KC_CLIENT=dummy
KC_CLIENT_SECRET=98732edc-573e-4a45-8fbc-0feee1ebcccb
KC_URL="http://localhost:8180/auth"

# Request Tokens for credentials
KC_RESPONSE=$( \
   curl -k -v \
        -d "username=$KC_USERNAME" \
        -d "password=$KC_PASSWORD" \
        -d 'grant_type=password' \
        -d "client_id=$KC_CLIENT" \
        -d "client_secret=$KC_CLIENT_SECRET" \
        "$KC_URL/realms/$KC_REALM/protocol/openid-connect/token" \
    | jq .
)

KC_ACCESS_TOKEN=$(echo $KC_RESPONSE| jq -r .access_token)
KC_ID_TOKEN=$(echo $KC_RESPONSE| jq -r .id_token)
KC_REFRESH_TOKEN=$(echo $KC_RESPONSE| jq -r .refresh_token)

Query /customers endpoint on database service with a valid access token

$ curl -H "Authorization: Bearer $KC_ACCESS_TOKEN" -v http://localhost:8080/keycloak-example-spring-security-war-0.0.1.BUILD-SNAPSHOT/customers
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /keycloak-example-spring-security-war-0.0.1.BUILD-SNAPSHOT/customers HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.46.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJoS19sRGJGUzFVekN0VnY1cE5oSVRXYy1nV1VIcENRelB1Ylp4NlRNdk1jIn0.eyJqdGkiOiI1MjA3YmYzMC05YTYwLTRjM2MtODE2OS02YzEzMjViMjFiYmUiLCJleHAiOjE0Nzk5NDAxNzUsIm5iZiI6MCwiaWF0IjoxNDc5OTM5ODc1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgxODAvYXV0aC9yZWFsbXMvc3ByaW5nLXNlY3VyaXR5LWFkYXB0ZXItdGVzdCIsImF1ZCI6ImR1bW15Iiwic3ViIjoiYWZjZjRmYWMtYWNlYy00MzU5LWJhZjYtNjJkMTZiMGFiZmVjIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiZHVtbXkiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI4NzNmZTg2NS05ZjMzLTRhZDQtODdjYS03NzllOGExNzcwODkiLCJhY3IiOiIxIiwiY2xpZW50X3Nlc3Npb24iOiJhYjlhMzExNy04NGI5LTQ5MTEtYjJmZS04MjJlNmFlZDM3ODYiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJkYXRhYmFzZS1jbGllbnQiOnsicm9sZXMiOlsidXNlciJdfSwiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsInZpZXctcHJvZmlsZSJdfX0sIm5hbWUiOiJUaGVvIFRlc3RlciIsInByZWZlcnJlZF91c2VybmFtZSI6InRlc3RlciIsImdpdmVuX25hbWUiOiJUaGVvIiwiZmFtaWx5X25hbWUiOiJUZXN0ZXIifQ.B8mTvA7YiaFj3vAwrwXM_7FVlPUy71bmDv32Fb0adh1wkFtWPR05HEdKklF_zxeeACYwy2jZ1iikeJHLAxHu7GAzT7DY1EfqStsbHMkgMNVJ-eX6KYQk31_paKpV-WuYFjg6X9joh2nisf9f0DWpcLojKDy_ORpvhWOUgKMUiMZLex3dHWhT5aAg-qtLZwH7VKxjQId84jaLGUjoZfZIFMsG49AJOvUAVTTvsd1et4SO_PaK8qdCMu0feHoQsP1irT2c18zHwMGmC4E6E7pG-jTl7bvDp7TprGfyfIlWkwF8iw29BvqCmRhnWvtUC4cdENQVjZsrAhRsA6S5EkLn4w
> 
< HTTP/1.1 200 OK
< Expires: 0
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< X-Powered-By: Undertow/1
< Server: JBoss-EAP/7
< X-XSS-Protection: 1; mode=block
< Pragma: no-cache
< X-Frame-Options: DENY
< Date: Wed, 23 Nov 2016 22:26:07 GMT
< Connection: keep-alive
< X-Content-Type-Options: nosniff
< Transfer-Encoding: chunked
< Content-Type: application/json;charset=UTF-8
< 
* Connection #0 to host localhost left intact
["Scott Rossillo","Kyung Lee","Keith Leggins","Ben Loy"]%                                                                                                                         
{
"realm": "spring-security-adapter-test",
"bearer-only": true,
"auth-server-url": "http://localhost:8180/auth",
"ssl-required": "external",
"resource": "database-client",
"use-resource-role-mappings": true
}
<?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>de.tdlabs</groupId>
<artifactId>keycloak-example-spring-security-war</artifactId>
<version>0.0.1.BUILD-SNAPSHOT</version>
<packaging>war</packaging>
<name>keycloak-example-spring-security-war</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.2.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-security-adapter</artifactId>
<version>2.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package de.tdlabs.examples;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.keycloak.adapters.springsecurity.filter.KeycloakAuthenticationProcessingFilter;
import org.keycloak.adapters.springsecurity.filter.KeycloakPreAuthActionsFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
/**
* Application security configuration.
*
* @author Scott Rossillo
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(false);
return registrationBean;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.sessionAuthenticationStrategy(sessionAuthenticationStrategy()).and()
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
.addFilterBefore(keycloakAuthenticationProcessingFilter(), X509AuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()).and().authorizeRequests()
.antMatchers("/**").authenticated().anyRequest().permitAll();
}
}
@maslick
Copy link

maslick commented Oct 11, 2017

how does the java app know about your Keycloak server? Do you put keycloak.json to resources (webapp) directory?

@obotor
Copy link

obotor commented Nov 24, 2017

Hey
@maslick: You have to specifically put your keycloak.json in /WEB-INF/keycloak.json. This is hard-coded in the adapter. Else you may use the SpringBoot adapter on top of SpringSecurity adapter and set the contents in the properties/yml file (with keycloak prefix).

@obotor
Copy link

obotor commented Nov 24, 2017

And you then will have to use the alternate KeycloakConfigResolver (pls refer to doc).

@developerForReal
Copy link

developerForReal commented Mar 13, 2018

Hi Thomas, can you help me to setup keycloak for securing my REST apis. So my goal is to secure all APIs in my project, user has to first get token and than he can access any with that token. So flow should be like this if user logging into website via login form and entering email and password so first call should to get access token and than he can log into the website and can access all modules. Using spring boot for this, please guide me to solve this.

@nikhilpatil1990
Copy link

Hi, I have created an application using the KeycloakWebSecurityConfigurerAdapter, but is it possible for the adapter to fetch server details from JBoss standalon.xml Subsytem, instead of Keycloak.json?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment