adapter for spring boot 2
remove built directory update snapshot version references refactor out core library to remove duplication adapter for spring boot 2 remove built directory update snapshot version references Revert "merge from upstream" This reverts commit 88c39a2f23b8f2d4b25360e2b46e683d11b4972b, reversing changes made to f0811145ceeb8ec609ed66b06067f797e288aa89. setting correct versions updating to latest keycloak arquillian test app for spring boot2 update to 2.0.0.RELEASE added Rest Customizer
This commit is contained in:
1 parent
fe98c30077
commit
d57fb445eb
34 files changed
+1831
-201
No files matched your search
@@ -42,6 +42,8 @@
|
||||
<module>servlet-filter</module>
|
||||
<module>servlet-oauth-client</module>
|
||||
<module>spring-boot</module>
|
||||
<module>spring-boot2</module>
|
||||
<module>spring-boot-adapter-core</module>
|
||||
<module>spring-boot-container-bundle</module>
|
||||
<module>spring-security</module>
|
||||
<module>tomcat</module>
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed 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.
|
||||
-->
|
||||
|
||||
<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>4.0.0.CR1-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-spring-boot-adapter-core</artifactId>
|
||||
<name>Keycloak Spring Boot Adapter Core</name>
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<spring-boot.version>1.3.0.RELEASE</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>spring-boot-container-bundle</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-servlet</artifactId>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>${jetty9.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-security</artifactId>
|
||||
<version>${jetty9.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
<version>${jetty9.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+262
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.springboot;
|
||||
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import io.undertow.servlet.api.WebResourceCollection;
|
||||
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.eclipse.jetty.security.ConstraintMapping;
|
||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.keycloak.adapters.jetty.KeycloakJettyAuthenticator;
|
||||
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||
import org.keycloak.adapters.undertow.KeycloakServletExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
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.jetty.JettyServerCustomizer;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.undertow.UndertowDeploymentInfoCustomizer;
|
||||
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.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Keycloak authentication base integration for Spring Boot - base to be extended for particular boot versions.
|
||||
*/
|
||||
public class KeycloakBaseSpringBootConfiguration {
|
||||
|
||||
protected KeycloakSpringBootProperties keycloakProperties;
|
||||
|
||||
@Autowired
|
||||
public void setKeycloakSpringBootProperties(KeycloakSpringBootProperties keycloakProperties) {
|
||||
this.keycloakProperties = keycloakProperties;
|
||||
KeycloakSpringBootConfigResolver.setAdapterConfig(keycloakProperties);
|
||||
}
|
||||
|
||||
|
||||
static class KeycloakBaseUndertowDeploymentInfoCustomizer {
|
||||
|
||||
protected final KeycloakSpringBootProperties keycloakProperties;
|
||||
|
||||
public KeycloakBaseUndertowDeploymentInfoCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||
this.keycloakProperties = keycloakProperties;
|
||||
}
|
||||
|
||||
public void customize(DeploymentInfo deploymentInfo) {
|
||||
|
||||
io.undertow.servlet.api.LoginConfig loginConfig = new io.undertow.servlet.api.LoginConfig(keycloakProperties.getRealm());
|
||||
loginConfig.addFirstAuthMethod("KEYCLOAK");
|
||||
|
||||
deploymentInfo.setLoginConfig(loginConfig);
|
||||
|
||||
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
||||
deploymentInfo.addSecurityConstraints(getSecurityConstraints());
|
||||
|
||||
deploymentInfo.addServletExtension(new KeycloakServletExtension());
|
||||
}
|
||||
|
||||
private List<io.undertow.servlet.api.SecurityConstraint> getSecurityConstraints() {
|
||||
|
||||
List<io.undertow.servlet.api.SecurityConstraint> undertowSecurityConstraints = new ArrayList<io.undertow.servlet.api.SecurityConstraint>();
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
|
||||
|
||||
io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint();
|
||||
undertowSecurityConstraint.addRolesAllowed(constraintDefinition.getAuthRoles());
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) {
|
||||
|
||||
WebResourceCollection webResourceCollection = new WebResourceCollection();
|
||||
webResourceCollection.addHttpMethods(collectionDefinition.getMethods());
|
||||
webResourceCollection.addHttpMethodOmissions(collectionDefinition.getOmittedMethods());
|
||||
webResourceCollection.addUrlPatterns(collectionDefinition.getPatterns());
|
||||
|
||||
undertowSecurityConstraint.addWebResourceCollections(webResourceCollection);
|
||||
|
||||
}
|
||||
|
||||
undertowSecurityConstraints.add(undertowSecurityConstraint);
|
||||
}
|
||||
return undertowSecurityConstraints;
|
||||
}
|
||||
}
|
||||
|
||||
static class KeycloakBaseJettyServerCustomizer {
|
||||
|
||||
protected final KeycloakSpringBootProperties keycloakProperties;
|
||||
|
||||
public KeycloakBaseJettyServerCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||
this.keycloakProperties = keycloakProperties;
|
||||
}
|
||||
|
||||
public void customize(Server server) {
|
||||
|
||||
KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator();
|
||||
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver());
|
||||
|
||||
/* see org.eclipse.jetty.webapp.StandardDescriptorProcessor#visitSecurityConstraint for an example
|
||||
on how to map servlet spec to Constraints */
|
||||
|
||||
List<ConstraintMapping> jettyConstraintMappings = new ArrayList<ConstraintMapping>();
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityCollection securityCollectionDefinition : constraintDefinition
|
||||
.getSecurityCollections()) {
|
||||
// securityCollection matches servlet spec's web-resource-collection
|
||||
Constraint jettyConstraint = new Constraint();
|
||||
|
||||
if (constraintDefinition.getAuthRoles().size() > 0) {
|
||||
jettyConstraint.setAuthenticate(true);
|
||||
jettyConstraint.setRoles(constraintDefinition.getAuthRoles().toArray(new String[0]));
|
||||
}
|
||||
|
||||
jettyConstraint.setName(securityCollectionDefinition.getName());
|
||||
|
||||
// according to the servlet spec each security-constraint has at least one URL pattern
|
||||
for(String pattern : securityCollectionDefinition.getPatterns()) {
|
||||
|
||||
/* the following code is asymmetric as Jetty's ConstraintMapping accepts only one allowed HTTP method,
|
||||
but multiple omitted methods. Therefore we add one ConstraintMapping for each allowed
|
||||
mapping but only one mapping in the cases of omitted methods or no methods.
|
||||
*/
|
||||
|
||||
if (securityCollectionDefinition.getMethods().size() > 0) {
|
||||
// according to the servlet spec we have either methods ...
|
||||
for(String method : securityCollectionDefinition.getMethods()) {
|
||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||
|
||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||
jettyConstraintMapping.setPathSpec(pattern);
|
||||
jettyConstraintMapping.setMethod(method);
|
||||
}
|
||||
} else if (securityCollectionDefinition.getOmittedMethods().size() > 0){
|
||||
// ... omitted methods ...
|
||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||
|
||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||
jettyConstraintMapping.setPathSpec(pattern);
|
||||
jettyConstraintMapping.setMethodOmissions(
|
||||
securityCollectionDefinition.getOmittedMethods().toArray(new String[0]));
|
||||
} else {
|
||||
// ... or no methods at all
|
||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||
|
||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||
jettyConstraintMapping.setPathSpec(pattern);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
WebAppContext webAppContext = server.getBean(WebAppContext.class);
|
||||
//if not found as registered bean let's try the handler
|
||||
if(webAppContext==null){
|
||||
webAppContext = (WebAppContext) server.getHandler();
|
||||
}
|
||||
|
||||
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
|
||||
securityHandler.setConstraintMappings(jettyConstraintMappings);
|
||||
securityHandler.setAuthenticator(keycloakJettyAuthenticator);
|
||||
|
||||
webAppContext.setSecurityHandler(securityHandler);
|
||||
}
|
||||
}
|
||||
|
||||
static class KeycloakBaseTomcatContextCustomizer {
|
||||
|
||||
protected final KeycloakSpringBootProperties keycloakProperties;
|
||||
|
||||
public KeycloakBaseTomcatContextCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||
this.keycloakProperties = keycloakProperties;
|
||||
}
|
||||
|
||||
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 (String authRole : constraint.getAuthRoles()) {
|
||||
if (!authRoles.contains(authRole)) {
|
||||
context.addSecurityRole(authRole);
|
||||
authRoles.add(authRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||
SecurityConstraint tomcatConstraint = new SecurityConstraint();
|
||||
|
||||
for (String authRole : constraint.getAuthRoles()) {
|
||||
tomcatConstraint.addAuthRole(authRole);
|
||||
}
|
||||
|
||||
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 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.keycloak.adapters.springboot.KeycloakAutoConfiguration
|
||||
@@ -37,6 +37,12 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-adapter-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
||||
+7
-199
@@ -17,20 +17,7 @@
|
||||
|
||||
package org.keycloak.adapters.springboot;
|
||||
|
||||
import io.undertow.servlet.api.DeploymentInfo;
|
||||
import io.undertow.servlet.api.WebResourceCollection;
|
||||
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.eclipse.jetty.security.ConstraintMapping;
|
||||
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.security.Constraint;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.keycloak.adapters.jetty.KeycloakJettyAuthenticator;
|
||||
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||
import org.keycloak.adapters.undertow.KeycloakServletExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
@@ -47,10 +34,6 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Keycloak authentication integration for Spring Boot
|
||||
@@ -62,7 +45,7 @@ import java.util.Set;
|
||||
@ConditionalOnWebApplication
|
||||
@EnableConfigurationProperties(KeycloakSpringBootProperties.class)
|
||||
@ConditionalOnProperty(value = "keycloak.enabled", matchIfMissing = true)
|
||||
public class KeycloakAutoConfiguration {
|
||||
public class KeycloakAutoConfiguration extends KeycloakBaseSpringBootConfiguration {
|
||||
|
||||
private KeycloakSpringBootProperties keycloakProperties;
|
||||
|
||||
@@ -117,202 +100,27 @@ public class KeycloakAutoConfiguration {
|
||||
return new KeycloakUndertowDeploymentInfoCustomizer(keycloakProperties);
|
||||
}
|
||||
|
||||
static class KeycloakUndertowDeploymentInfoCustomizer implements UndertowDeploymentInfoCustomizer {
|
||||
|
||||
private final KeycloakSpringBootProperties keycloakProperties;
|
||||
static class KeycloakUndertowDeploymentInfoCustomizer extends KeycloakBaseUndertowDeploymentInfoCustomizer implements UndertowDeploymentInfoCustomizer {
|
||||
|
||||
public KeycloakUndertowDeploymentInfoCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||
this.keycloakProperties = keycloakProperties;
|
||||
super(keycloakProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(DeploymentInfo deploymentInfo) {
|
||||
|
||||
io.undertow.servlet.api.LoginConfig loginConfig = new io.undertow.servlet.api.LoginConfig(keycloakProperties.getRealm());
|
||||
loginConfig.addFirstAuthMethod("KEYCLOAK");
|
||||
|
||||
deploymentInfo.setLoginConfig(loginConfig);
|
||||
deploymentInfo.addInitParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
||||
deploymentInfo.addSecurityConstraints(getSecurityConstraints());
|
||||
deploymentInfo.addServletExtension(new KeycloakServletExtension());
|
||||
}
|
||||
|
||||
private List<io.undertow.servlet.api.SecurityConstraint> getSecurityConstraints() {
|
||||
|
||||
List<io.undertow.servlet.api.SecurityConstraint> undertowSecurityConstraints = new ArrayList<io.undertow.servlet.api.SecurityConstraint>();
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
|
||||
|
||||
io.undertow.servlet.api.SecurityConstraint undertowSecurityConstraint = new io.undertow.servlet.api.SecurityConstraint();
|
||||
undertowSecurityConstraint.addRolesAllowed(constraintDefinition.getAuthRoles());
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityCollection collectionDefinition : constraintDefinition.getSecurityCollections()) {
|
||||
|
||||
WebResourceCollection webResourceCollection = new WebResourceCollection();
|
||||
webResourceCollection.addHttpMethods(collectionDefinition.getMethods());
|
||||
webResourceCollection.addHttpMethodOmissions(collectionDefinition.getOmittedMethods());
|
||||
webResourceCollection.addUrlPatterns(collectionDefinition.getPatterns());
|
||||
|
||||
undertowSecurityConstraint.addWebResourceCollections(webResourceCollection);
|
||||
|
||||
}
|
||||
undertowSecurityConstraints.add(undertowSecurityConstraint);
|
||||
}
|
||||
return undertowSecurityConstraints;
|
||||
}
|
||||
}
|
||||
|
||||
static class KeycloakJettyServerCustomizer implements JettyServerCustomizer {
|
||||
|
||||
private final KeycloakSpringBootProperties keycloakProperties;
|
||||
static class KeycloakJettyServerCustomizer extends KeycloakBaseJettyServerCustomizer implements JettyServerCustomizer {
|
||||
|
||||
public KeycloakJettyServerCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||
this.keycloakProperties = keycloakProperties;
|
||||
super(keycloakProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(Server server) {
|
||||
|
||||
KeycloakJettyAuthenticator keycloakJettyAuthenticator = new KeycloakJettyAuthenticator();
|
||||
keycloakJettyAuthenticator.setConfigResolver(new KeycloakSpringBootConfigResolver());
|
||||
|
||||
/* see org.eclipse.jetty.webapp.StandardDescriptorProcessor#visitSecurityConstraint for an example
|
||||
on how to map servlet spec to Constraints */
|
||||
|
||||
List<ConstraintMapping> jettyConstraintMappings = new ArrayList<ConstraintMapping>();
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraintDefinition : keycloakProperties.getSecurityConstraints()) {
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityCollection securityCollectionDefinition : constraintDefinition
|
||||
.getSecurityCollections()) {
|
||||
// securityCollection matches servlet spec's web-resource-collection
|
||||
Constraint jettyConstraint = new Constraint();
|
||||
|
||||
if (constraintDefinition.getAuthRoles().size() > 0) {
|
||||
jettyConstraint.setAuthenticate(true);
|
||||
jettyConstraint.setRoles(constraintDefinition.getAuthRoles().toArray(new String[0]));
|
||||
}
|
||||
|
||||
jettyConstraint.setName(securityCollectionDefinition.getName());
|
||||
|
||||
// according to the servlet spec each security-constraint has at least one URL pattern
|
||||
for(String pattern : securityCollectionDefinition.getPatterns()) {
|
||||
|
||||
/* the following code is asymmetric as Jetty's ConstraintMapping accepts only one allowed HTTP method,
|
||||
but multiple omitted methods. Therefore we add one ConstraintMapping for each allowed
|
||||
mapping but only one mapping in the cases of omitted methods or no methods.
|
||||
*/
|
||||
|
||||
if (securityCollectionDefinition.getMethods().size() > 0) {
|
||||
// according to the servlet spec we have either methods ...
|
||||
for(String method : securityCollectionDefinition.getMethods()) {
|
||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||
|
||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||
jettyConstraintMapping.setPathSpec(pattern);
|
||||
jettyConstraintMapping.setMethod(method);
|
||||
}
|
||||
} else if (securityCollectionDefinition.getOmittedMethods().size() > 0){
|
||||
// ... omitted methods ...
|
||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||
|
||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||
jettyConstraintMapping.setPathSpec(pattern);
|
||||
jettyConstraintMapping.setMethodOmissions(
|
||||
securityCollectionDefinition.getOmittedMethods().toArray(new String[0]));
|
||||
} else {
|
||||
// ... or no methods at all
|
||||
ConstraintMapping jettyConstraintMapping = new ConstraintMapping();
|
||||
jettyConstraintMappings.add(jettyConstraintMapping);
|
||||
|
||||
jettyConstraintMapping.setConstraint(jettyConstraint);
|
||||
jettyConstraintMapping.setPathSpec(pattern);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
WebAppContext webAppContext = server.getBean(WebAppContext.class);
|
||||
//if not found as registered bean let's try the handler
|
||||
if(webAppContext==null){
|
||||
webAppContext = (WebAppContext) server.getHandler();
|
||||
}
|
||||
|
||||
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
|
||||
securityHandler.setConstraintMappings(jettyConstraintMappings);
|
||||
securityHandler.setAuthenticator(keycloakJettyAuthenticator);
|
||||
|
||||
webAppContext.setSecurityHandler(securityHandler);
|
||||
}
|
||||
}
|
||||
|
||||
static class KeycloakTomcatContextCustomizer implements TomcatContextCustomizer {
|
||||
|
||||
private final KeycloakSpringBootProperties keycloakProperties;
|
||||
static class KeycloakTomcatContextCustomizer extends KeycloakBaseTomcatContextCustomizer implements TomcatContextCustomizer {
|
||||
|
||||
public KeycloakTomcatContextCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||
this.keycloakProperties = keycloakProperties;
|
||||
super(keycloakProperties);
|
||||
}
|
||||
|
||||
@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 (String authRole : constraint.getAuthRoles()) {
|
||||
if (!authRoles.contains(authRole)) {
|
||||
context.addSecurityRole(authRole);
|
||||
authRoles.add(authRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||
SecurityConstraint tomcatConstraint = new SecurityConstraint();
|
||||
|
||||
for (String authRole : constraint.getAuthRoles()) {
|
||||
tomcatConstraint.addAuthRole(authRole);
|
||||
if(authRole.equals("*") || authRole.equals("**")) {
|
||||
// For some reasons embed tomcat don't set the auth constraint on true when wildcard is
|
||||
// used
|
||||
tomcatConstraint.setAuthConstraint(true);
|
||||
}
|
||||
}
|
||||
|
||||
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 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+151
@@ -0,0 +1,151 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed 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.
|
||||
-->
|
||||
|
||||
<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>4.0.0.CR1-SNAPSHOT</version>
|
||||
<relativePath>../../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-spring-boot-2-adapter</artifactId>
|
||||
<name>Keycloak Spring Boot 2 Integration</name>
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<spring-boot.version>2.0.0.RELEASE</spring-boot.version>
|
||||
<spring.version>5.0.2.RELEASE</spring.version>
|
||||
<mockito.version>1.9.5</mockito.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-adapter-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>spring-boot-container-bundle</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-security-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
<version>2.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<version>5.0.2.RELEASE</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.undertow</groupId>
|
||||
<artifactId>undertow-servlet</artifactId>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<version>${jetty9.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-security</artifactId>
|
||||
<version>${jetty9.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
<version>${jetty9.version}</version>
|
||||
<scope>compile</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.springboot;
|
||||
|
||||
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
|
||||
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowDeploymentInfoCustomizer;
|
||||
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Keycloak authentication integration for Spring Boot 2
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
@EnableConfigurationProperties(KeycloakSpringBootProperties.class)
|
||||
@ConditionalOnProperty(value = "keycloak.enabled", matchIfMissing = true)
|
||||
public class KeycloakAutoConfiguration extends KeycloakBaseSpringBootConfiguration {
|
||||
|
||||
|
||||
@Bean
|
||||
public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> getKeycloakContainerCustomizer() {
|
||||
return new WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>() {
|
||||
@Override
|
||||
public void customize(ConfigurableServletWebServerFactory configurableServletWebServerFactory) {
|
||||
if(configurableServletWebServerFactory instanceof TomcatServletWebServerFactory){
|
||||
|
||||
TomcatServletWebServerFactory container = (TomcatServletWebServerFactory)configurableServletWebServerFactory;
|
||||
container.addContextValves(new KeycloakAuthenticatorValve());
|
||||
container.addContextCustomizers(tomcatKeycloakContextCustomizer());
|
||||
|
||||
} else if (configurableServletWebServerFactory instanceof UndertowServletWebServerFactory){
|
||||
|
||||
UndertowServletWebServerFactory container = (UndertowServletWebServerFactory)configurableServletWebServerFactory;
|
||||
container.addDeploymentInfoCustomizers(undertowKeycloakContextCustomizer());
|
||||
|
||||
} else if (configurableServletWebServerFactory instanceof JettyServletWebServerFactory){
|
||||
|
||||
JettyServletWebServerFactory container = (JettyServletWebServerFactory)configurableServletWebServerFactory;
|
||||
container.addServerCustomizers(jettyKeycloakServerCustomizer());
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = {"org.eclipse.jetty.webapp.WebAppContext"})
|
||||
public JettyServerCustomizer jettyKeycloakServerCustomizer() {
|
||||
return new KeycloakJettyServerCustomizer(keycloakProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = {"org.apache.catalina.startup.Tomcat"})
|
||||
public TomcatContextCustomizer tomcatKeycloakContextCustomizer() {
|
||||
return new KeycloakTomcatContextCustomizer(keycloakProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = {"io.undertow.Undertow"})
|
||||
public UndertowDeploymentInfoCustomizer undertowKeycloakContextCustomizer() {
|
||||
return new KeycloakUndertowDeploymentInfoCustomizer(keycloakProperties);
|
||||
}
|
||||
|
||||
static class KeycloakJettyServerCustomizer extends KeycloakBaseJettyServerCustomizer implements JettyServerCustomizer {
|
||||
|
||||
|
||||
public KeycloakJettyServerCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||
super(keycloakProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class KeycloakTomcatContextCustomizer extends KeycloakBaseTomcatContextCustomizer implements TomcatContextCustomizer {
|
||||
|
||||
public KeycloakTomcatContextCustomizer(KeycloakSpringBootProperties keycloakProperties) {
|
||||
super(keycloakProperties);
|
||||
}
|
||||
}
|
||||
|
||||
static class KeycloakUndertowDeploymentInfoCustomizer extends KeycloakBaseUndertowDeploymentInfoCustomizer implements UndertowDeploymentInfoCustomizer {
|
||||
|
||||
public KeycloakUndertowDeploymentInfoCustomizer(KeycloakSpringBootProperties keycloakProperties){
|
||||
super(keycloakProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package org.keycloak.adapters.springboot.client;
|
||||
|
||||
import org.springframework.boot.web.client.RestTemplateCustomizer;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
public class KeycloakRestTemplateCustomizer implements RestTemplateCustomizer {
|
||||
|
||||
private final KeycloakSecurityContextClientRequestInterceptor keycloakInterceptor;
|
||||
|
||||
public KeycloakRestTemplateCustomizer() {
|
||||
this(new KeycloakSecurityContextClientRequestInterceptor());
|
||||
}
|
||||
|
||||
protected KeycloakRestTemplateCustomizer(
|
||||
KeycloakSecurityContextClientRequestInterceptor keycloakInterceptor
|
||||
) {
|
||||
this.keycloakInterceptor = keycloakInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(RestTemplate restTemplate) {
|
||||
restTemplate.getInterceptors().add(keycloakInterceptor);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package org.keycloak.adapters.springboot.client;
|
||||
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* Interceptor for {@link ClientHttpRequestExecution} objects created for server to server secured
|
||||
* communication using OAuth2 bearer tokens issued by Keycloak.
|
||||
*
|
||||
* @author <a href="mailto:jmcshan1@gmail.com">James McShane</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class KeycloakSecurityContextClientRequestInterceptor implements ClientHttpRequestInterceptor {
|
||||
|
||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
/**
|
||||
* Returns the {@link KeycloakSecurityContext} from the Spring {@link ServletRequestAttributes}'s {@link Principal}.
|
||||
*
|
||||
* The principal must support retrieval of the KeycloakSecurityContext, so at this point, only {@link KeycloakPrincipal}
|
||||
* values are supported
|
||||
*
|
||||
* @return the current <code>KeycloakSecurityContext</code>
|
||||
*/
|
||||
protected KeycloakSecurityContext getKeycloakSecurityContext() {
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
Principal principal = attributes.getRequest().getUserPrincipal();
|
||||
if (principal == null) {
|
||||
throw new IllegalStateException("Cannot set authorization header because there is no authenticated principal");
|
||||
}
|
||||
if (!(principal instanceof KeycloakPrincipal)) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Cannot set authorization header because the principal type %s does not provide the KeycloakSecurityContext",
|
||||
principal.getClass()));
|
||||
}
|
||||
return ((KeycloakPrincipal) principal).getKeycloakSecurityContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
|
||||
KeycloakSecurityContext context = this.getKeycloakSecurityContext();
|
||||
httpRequest.getHeaders().set(AUTHORIZATION_HEADER, "Bearer " + context.getTokenString());
|
||||
return clientHttpRequestExecution.execute(httpRequest, bytes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.keycloak.adapters.springboot.KeycloakAutoConfiguration
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package org.keycloak.adapters.springboot.client;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class KeycloakRestTemplateCustomizerTest {
|
||||
|
||||
private KeycloakRestTemplateCustomizer customizer;
|
||||
private KeycloakSecurityContextClientRequestInterceptor interceptor =
|
||||
mock(KeycloakSecurityContextClientRequestInterceptor.class);
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
customizer = new KeycloakRestTemplateCustomizer(interceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interceptorIsAddedToRequest() {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
customizer.customize(restTemplate);
|
||||
assertTrue(restTemplate.getInterceptors().contains(interceptor));
|
||||
}
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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.
|
||||
*/
|
||||
|
||||
package org.keycloak.adapters.springboot.client;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.KeycloakPrincipal;
|
||||
import org.keycloak.KeycloakSecurityContext;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.Spy;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Keycloak spring boot client request factory tests.
|
||||
*/
|
||||
public class KeycloakSecurityContextClientRequestInterceptorTest {
|
||||
|
||||
@Spy
|
||||
private KeycloakSecurityContextClientRequestInterceptor factory;
|
||||
|
||||
private MockHttpServletRequest servletRequest;
|
||||
|
||||
@Mock
|
||||
private KeycloakSecurityContext keycloakSecurityContext;
|
||||
|
||||
@Mock
|
||||
private KeycloakPrincipal keycloakPrincipal;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
servletRequest = new MockHttpServletRequest();
|
||||
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(servletRequest));
|
||||
servletRequest.setUserPrincipal(keycloakPrincipal);
|
||||
when(keycloakPrincipal.getKeycloakSecurityContext()).thenReturn(keycloakSecurityContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetKeycloakSecurityContext() throws Exception {
|
||||
KeycloakSecurityContext context = factory.getKeycloakSecurityContext();
|
||||
assertNotNull(context);
|
||||
assertEquals(keycloakSecurityContext, context);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testGetKeycloakSecurityContextInvalidPrincipal() throws Exception {
|
||||
servletRequest.setUserPrincipal(new MarkerPrincipal());
|
||||
factory.getKeycloakSecurityContext();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testGetKeycloakSecurityContextNullAuthentication() throws Exception {
|
||||
servletRequest.setUserPrincipal(null);
|
||||
factory.getKeycloakSecurityContext();
|
||||
}
|
||||
|
||||
private static class MarkerPrincipal implements Principal {
|
||||
@Override
|
||||
public String getName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user