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 Jetty8Adapter SYSTEM "modules/jetty8-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 Logout SYSTEM "modules/logout.xml">
|
||||
<!ENTITY SAML SYSTEM "modules/saml.xml">
|
||||
|
@ -95,6 +96,7 @@ This one is short
|
|||
&Jetty8Adapter;
|
||||
&FuseAdapter;
|
||||
&JavascriptAdapter;
|
||||
&SpringBootAdapter;
|
||||
&InstalledApplications;
|
||||
&Logout;
|
||||
&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>admin-client</module>
|
||||
<module>osgi-adapter</module>
|
||||
<module>spring-boot</module>
|
||||
</modules>
|
||||
</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