Merge pull request #916 from jimmidyson/spring-boot-integration
Spring boot adapter
This commit is contained in:
commit
42bdb7731d
8 changed files with 395 additions and 0 deletions
|
@ -15,6 +15,7 @@
|
||||||
<!ENTITY Jetty9Adapter SYSTEM "modules/jetty9-adapter.xml">
|
<!ENTITY Jetty9Adapter SYSTEM "modules/jetty9-adapter.xml">
|
||||||
<!ENTITY Jetty8Adapter SYSTEM "modules/jetty8-adapter.xml">
|
<!ENTITY Jetty8Adapter SYSTEM "modules/jetty8-adapter.xml">
|
||||||
<!ENTITY FuseAdapter SYSTEM "modules/fuse-adapter.xml">
|
<!ENTITY FuseAdapter SYSTEM "modules/fuse-adapter.xml">
|
||||||
|
<!ENTITY SpringBootAdapter SYSTEM "modules/spring-boot-adapter.xml">
|
||||||
<!ENTITY InstalledApplications SYSTEM "modules/installed-applications.xml">
|
<!ENTITY InstalledApplications SYSTEM "modules/installed-applications.xml">
|
||||||
<!ENTITY Logout SYSTEM "modules/logout.xml">
|
<!ENTITY Logout SYSTEM "modules/logout.xml">
|
||||||
<!ENTITY SAML SYSTEM "modules/saml.xml">
|
<!ENTITY SAML SYSTEM "modules/saml.xml">
|
||||||
|
@ -95,6 +96,7 @@ This one is short
|
||||||
&Jetty8Adapter;
|
&Jetty8Adapter;
|
||||||
&FuseAdapter;
|
&FuseAdapter;
|
||||||
&JavascriptAdapter;
|
&JavascriptAdapter;
|
||||||
|
&SpringBootAdapter;
|
||||||
&InstalledApplications;
|
&InstalledApplications;
|
||||||
&Logout;
|
&Logout;
|
||||||
&MultiTenancy;
|
&MultiTenancy;
|
||||||
|
|
72
docbook/reference/en/en-US/modules/spring-boot-adapter.xml
Executable file
72
docbook/reference/en/en-US/modules/spring-boot-adapter.xml
Executable file
|
@ -0,0 +1,72 @@
|
||||||
|
<section id="spring-boot-adapter">
|
||||||
|
<title>Spring Boot Adapter</title>
|
||||||
|
<para>
|
||||||
|
To be able to secure Spring Boot apps you must add the Keycloak Spring Boot adapter
|
||||||
|
JAR to your app. You then have to provide some extra configuration via normal Spring
|
||||||
|
Boot configuration (<literal>application.properties</literal>). Let's go over these steps.
|
||||||
|
</para>
|
||||||
|
<section id="spring-boot-adapter-installation">
|
||||||
|
<title>Adapter Installation</title>
|
||||||
|
<para>
|
||||||
|
The Keycloak Spring Boot adapter takes advantage of Spring Boot's autoconfiguration so all
|
||||||
|
you need to do is add the Keycloak Spring Boot adapter JAR to your project. Depending on
|
||||||
|
what container you are using with Spring Boot, you also need to add the appropriate
|
||||||
|
Keycloak container adapter. If you are using Maven, add the following to your pom.xml (using
|
||||||
|
Tomcat as an example):
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
||||||
|
<version>1.2.0.Beta1-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-tomcat8-adapter</artifactId>
|
||||||
|
<version>${keycloak.version}</version>
|
||||||
|
</dependency>
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="spring-boot-adapter-configuration">
|
||||||
|
<title>Required Spring Boot Adapter Configuration</title>
|
||||||
|
<para>
|
||||||
|
This section describes how to configure your Spring Boot app to use Keycloak.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Instead of a <literal>keycloak.json</literal> file, you configure the realm for the Spring
|
||||||
|
Boot Keycloak adapter via the normal Spring Boot configuration. For example:
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
keycloak.realm = demorealm
|
||||||
|
keycloak.realmKey = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLCWYuxXmsmfV+Xc9Ik8QET8lD4wuHrJAXbbutS2O/eMjQQLNK7QDX/k/XhOkhxP0YBEypqeXeGaeQJjCxDhFjJXQuewUEMlmSja3IpoJ9/hFn4Cns4m7NGO+rtvnfnwgVfsEOS5EmZhRddp+40KBPPJfTH6Vgu6KjQwuFPj6DTwIDAQAB
|
||||||
|
keycloak.auth-server-url = http://127.0.0.1:8080/auth
|
||||||
|
keycloak.ssl-required = external
|
||||||
|
keycloak.resource = demoapp
|
||||||
|
keycloak.credentials.secret = 11111111-1111-1111-1111-111111111111
|
||||||
|
keycloak.use-resource-role-mappings = true
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
You also need to specify the J2EE security config that would normally go in the <literal>web.xml</literal>.
|
||||||
|
Here's an example configuration:
|
||||||
|
</para>
|
||||||
|
<programlisting>
|
||||||
|
<![CDATA[
|
||||||
|
keycloak.securityConstraints[0].securityCollections[0].name = insecure stuff
|
||||||
|
keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = admin
|
||||||
|
keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = user
|
||||||
|
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /insecure
|
||||||
|
|
||||||
|
keycloak.securityConstraints[0].securityCollections[1].name = admin stuff
|
||||||
|
keycloak.securityConstraints[0].securityCollections[1].authRoles[0] = admin
|
||||||
|
keycloak.securityConstraints[0].securityCollections[1].patterns[0] = /admin
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
</section>
|
||||||
|
</section>
|
|
@ -29,5 +29,6 @@
|
||||||
<module>installed</module>
|
<module>installed</module>
|
||||||
<module>admin-client</module>
|
<module>admin-client</module>
|
||||||
<module>osgi-adapter</module>
|
<module>osgi-adapter</module>
|
||||||
|
<module>spring-boot</module>
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
||||||
|
|
77
integration/spring-boot/pom.xml
Executable file
77
integration/spring-boot/pom.xml
Executable file
|
@ -0,0 +1,77 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<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/maven-v4_0_0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>1.2.0.Beta1-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
||||||
|
<name>Keycloak Spring Boot Integration</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring-boot.version>1.2.1.RELEASE</spring-boot.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
<version>${jboss.logging.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-tomcat8-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-jetty92-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>${spring-boot.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.6</source>
|
||||||
|
<target>1.6</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,28 @@
|
||||||
|
package org.keycloak.adapters.springboot;
|
||||||
|
|
||||||
|
import org.keycloak.adapters.HttpFacade;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||||
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
|
|
||||||
|
public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.KeycloakConfigResolver {
|
||||||
|
|
||||||
|
private KeycloakDeployment keycloakDeployment;
|
||||||
|
|
||||||
|
private static AdapterConfig adapterConfig;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeycloakDeployment resolve(HttpFacade.Request request) {
|
||||||
|
if (keycloakDeployment != null) {
|
||||||
|
return keycloakDeployment;
|
||||||
|
}
|
||||||
|
|
||||||
|
keycloakDeployment = KeycloakDeploymentBuilder.build(KeycloakSpringBootConfigResolver.adapterConfig);
|
||||||
|
|
||||||
|
return keycloakDeployment;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setAdapterConfig(AdapterConfig adapterConfig) {
|
||||||
|
KeycloakSpringBootConfigResolver.adapterConfig = adapterConfig;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package org.keycloak.adapters.springboot;
|
||||||
|
|
||||||
|
import org.apache.catalina.Context;
|
||||||
|
import org.apache.tomcat.util.descriptor.web.LoginConfig;
|
||||||
|
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
|
||||||
|
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
|
||||||
|
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||||
|
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||||
|
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||||
|
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
||||||
|
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||||
|
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keycloak authentication integration for Spring Boot
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:jimmidyson@gmail.com">Jimmi Dyson</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnWebApplication
|
||||||
|
@EnableConfigurationProperties(KeycloakSpringBootProperties.class)
|
||||||
|
public class KeycloakSpringBootConfiguration {
|
||||||
|
|
||||||
|
private KeycloakSpringBootProperties keycloakProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setKeycloakSpringBootProperties(KeycloakSpringBootProperties keycloakProperties) {
|
||||||
|
this.keycloakProperties = keycloakProperties;
|
||||||
|
KeycloakSpringBootConfigResolver.setAdapterConfig(keycloakProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public EmbeddedServletContainerCustomizer getKeycloakContainerCustomizer() {
|
||||||
|
return new EmbeddedServletContainerCustomizer() {
|
||||||
|
@Override
|
||||||
|
public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
|
||||||
|
if (configurableEmbeddedServletContainer instanceof TomcatEmbeddedServletContainerFactory) {
|
||||||
|
TomcatEmbeddedServletContainerFactory container = (TomcatEmbeddedServletContainerFactory) configurableEmbeddedServletContainer;
|
||||||
|
|
||||||
|
container.addContextValves(new KeycloakAuthenticatorValve());
|
||||||
|
|
||||||
|
container.addContextCustomizers(getTomcatKeycloakContextCustomizer());
|
||||||
|
} else if (configurableEmbeddedServletContainer instanceof UndertowEmbeddedServletContainerFactory) {
|
||||||
|
throw new IllegalArgumentException("Undertow Keycloak integration is not yet implemented");
|
||||||
|
} else if (configurableEmbeddedServletContainer instanceof JettyEmbeddedServletContainerFactory) {
|
||||||
|
throw new IllegalArgumentException("Jetty Keycloak integration is not yet implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public TomcatContextCustomizer getTomcatKeycloakContextCustomizer() {
|
||||||
|
return new TomcatContextCustomizer() {
|
||||||
|
@Override
|
||||||
|
public void customize(Context context) {
|
||||||
|
LoginConfig loginConfig = new LoginConfig();
|
||||||
|
loginConfig.setAuthMethod("KEYCLOAK");
|
||||||
|
context.setLoginConfig(loginConfig);
|
||||||
|
|
||||||
|
Set<String> authRoles = new HashSet<String>();
|
||||||
|
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||||
|
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
|
||||||
|
for (String authRole : collection.getAuthRoles()) {
|
||||||
|
if (!authRoles.contains(authRole)) {
|
||||||
|
context.addSecurityRole(authRole);
|
||||||
|
authRoles.add(authRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||||
|
SecurityConstraint tomcatConstraint = new SecurityConstraint();
|
||||||
|
|
||||||
|
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
|
||||||
|
SecurityCollection tomcatSecCollection = new SecurityCollection();
|
||||||
|
|
||||||
|
if (collection.getName() != null) {
|
||||||
|
tomcatSecCollection.setName(collection.getName());
|
||||||
|
}
|
||||||
|
if (collection.getDescription() != null) {
|
||||||
|
tomcatSecCollection.setDescription(collection.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String authRole : collection.getAuthRoles()) {
|
||||||
|
tomcatConstraint.addAuthRole(authRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String pattern : collection.getPatterns()) {
|
||||||
|
tomcatSecCollection.addPattern(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String method : collection.getMethods()) {
|
||||||
|
tomcatSecCollection.addMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String method : collection.getOmittedMethods()) {
|
||||||
|
tomcatSecCollection.addOmittedMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
tomcatConstraint.addCollection(tomcatSecCollection);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.addConstraint(tomcatConstraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package org.keycloak.adapters.springboot;
|
||||||
|
|
||||||
|
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "keycloak", ignoreUnknownFields = false)
|
||||||
|
public class KeycloakSpringBootProperties extends AdapterConfig {
|
||||||
|
|
||||||
|
private List<SecurityConstraint> securityConstraints = new ArrayList<SecurityConstraint>();
|
||||||
|
|
||||||
|
public static class SecurityConstraint {
|
||||||
|
private List<SecurityCollection> securityCollections = new ArrayList<SecurityCollection>();
|
||||||
|
|
||||||
|
public List<SecurityCollection> getSecurityCollections() {
|
||||||
|
return securityCollections;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecurityCollections(List<SecurityCollection> securityCollections) {
|
||||||
|
this.securityCollections = securityCollections;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SecurityCollection {
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private List<String> authRoles = new ArrayList<String>();
|
||||||
|
private List<String> patterns = new ArrayList<String>();
|
||||||
|
private List<String> methods = new ArrayList<String>();
|
||||||
|
private List<String> omittedMethods = new ArrayList<String>();
|
||||||
|
|
||||||
|
public List<String> getAuthRoles() {
|
||||||
|
return authRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getPatterns() {
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getMethods() {
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getOmittedMethods() {
|
||||||
|
return omittedMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAuthRoles(List<String> authRoles) {
|
||||||
|
this.authRoles = authRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPatterns(List<String> patterns) {
|
||||||
|
this.patterns = patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethods(List<String> methods) {
|
||||||
|
this.methods = methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOmittedMethods(List<String> omittedMethods) {
|
||||||
|
this.omittedMethods = omittedMethods;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SecurityConstraint> getSecurityConstraints() {
|
||||||
|
return securityConstraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecurityConstraints(List<SecurityConstraint> securityConstraints) {
|
||||||
|
this.securityConstraints = securityConstraints;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration
|
Loading…
Reference in a new issue