forked from keycloak/keycloak
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
42 changed files
with
1,140 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<section id="multi_tenancy"> | ||
<title>Multi Tenancy</title> | ||
<para> | ||
Multi Tenancy, in our context, means that one single target application (WAR) can be secured by a single (or clustered) Keycloak server, authenticating | ||
its users against different realms. In practice, this means that one application needs to use different <literal>keycloak.json</literal> files. | ||
For this case, there are two possible solutions: | ||
<itemizedlist> | ||
|
||
<listitem> | ||
The same WAR file deployed under two different names, each with its own Keycloak configuration (probably via the Keycloak Subsystem). | ||
This scenario is suitable when the number of realms is known in advance or when there's a dynamic provision of application instances. | ||
One example would be a service provider that dinamically creates servers/deployments for their clients, like a PaaS. | ||
</listitem> | ||
|
||
<listitem> | ||
A WAR file deployed once (possibly in a cluster), that decides which realm to authenticate against based on the request parameters. | ||
This scenario is suitable when there are an undefined number of realms. One example would be a SaaS provider that have only one deployment | ||
(perhaps in a cluster) serving several companies, differentiating between clients based on the hostname | ||
(<literal>client1.acme.com</literal>, <literal>client2.acme.com</literal>) or path (<literal>/app/client1/</literal>, | ||
<literal>/app/client2/</literal>) or even via a special HTTP Header. | ||
</listitem> | ||
|
||
</itemizedlist> | ||
|
||
This chapter of the reference guide focus on this second scenario. | ||
</para> | ||
|
||
<para> | ||
Keycloak provides an extension point for applications that need to evaluate the realm on a request basis. During the authentication | ||
and authorization phase of the incoming request, Keycloak queries the application via this extension point and expects the application | ||
to return a complete representation of the realm. With this, Keycloak then proceeds the authentication and authorization process, | ||
accepting or refusing the request based on the incoming credentials and on the returned realm. | ||
|
||
For this scenario, an application needs to: | ||
|
||
<itemizedlist> | ||
|
||
<listitem> | ||
Add a context parameter to the <literal>web.xml</literal>, named <literal>keycloak.config.resolver</literal>. | ||
The value of this property should be the fully qualified name of the a class extending | ||
<literal>org.keycloak.adapters.KeycloakConfigResolver</literal>. | ||
</listitem> | ||
|
||
<listitem> | ||
A concrete implementation of <literal>org.keycloak.adapters.KeycloakConfigResolver</literal>. Keycloak will call the | ||
<literal>resolve(org.keycloak.adapters.HttpFacade.Request)</literal> method and expects a complete | ||
<literal>org.keycloak.adapters.KeycloakDeployment</literal> in response. Note that Keycloak will call this for every request, | ||
so, take the usual performance precautions. | ||
</listitem> | ||
|
||
</itemizedlist> | ||
</para> | ||
<para> | ||
An implementation of this feature can be found on the examples. | ||
</para> | ||
</section> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
Keycloak Example - Multi Tenancy | ||
======================================= | ||
|
||
The following example was tested on Wildfly 8.1.0.Final and should be compatible with any JBoss AS, JBoss EAP or Wildfly that supports Java EE 7. | ||
|
||
This example demonstrates the simplest possible scenario for Keycloak Multi Tenancy support. Multi Tenancy is understood on this context as a single application (WAR) that is deployed on a single or clustered application server, authenticating users from *different realms* against a single or clustered Keycloak server. | ||
|
||
The multi tenancy is achieved by having one realm per tenant on the server side and a per-request decision on which realm to authenticate the request against. | ||
|
||
This example contains only the minimal bits required for a multi tenant application. | ||
|
||
This example is composed of the following parts: | ||
|
||
- ProtectedServlet - A servlet that displays the username and realm from the current user | ||
- PathBasedKeycloakConfigResolver - A configuration resolver that takes the realm based on the path: /simple-multitenant/tenant2 means that the realm is "tenant2". | ||
|
||
Step 1: Setup a basic Keycloak server | ||
-------------------------------------------------------------- | ||
Install Keycloak server and start it on port 8080. Check the Reference Guide if unsure on how to do it. | ||
|
||
Once the Keycloak server is up and running, import the two realms from "src/main/resources/", namely: | ||
|
||
- tenant1-realm.json | ||
- tenant2-realm.json | ||
|
||
Step 2: Deploy and run the example | ||
-------------------------------------------------------------- | ||
|
||
- Build and deploy this sample's WAR file. For this example, deploy on the same server that is running the Keycloak Server, although this is not required for real world scenarios. | ||
- Access [http://localhost:8080/multitenant/tenant1](http://localhost:8080/multitenant/tenant1) and login as ``user-tenant1``, password ``user-tenant1`` | ||
- Access [http://localhost:8080/multitenant/tenant2](http://localhost:8080/multitenant/tenant2) and login as ``user-tenant2``, password ``user-tenant2`` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
<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> | ||
|
||
<parent> | ||
<artifactId>keycloak-parent</artifactId> | ||
<groupId>org.keycloak</groupId> | ||
<version>1.1.0-Alpha1-SNAPSHOT</version> | ||
<relativePath>../../pom.xml</relativePath> | ||
</parent> | ||
|
||
<name>Keycloak Examples - Multi Tenant</name> | ||
<artifactId>multitenant</artifactId> | ||
<packaging>war</packaging> | ||
|
||
<description> | ||
Keycloak Multi Tenants Example | ||
</description> | ||
|
||
<dependencyManagement> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.wildfly.bom</groupId> | ||
<artifactId>jboss-javaee-7.0-with-all</artifactId> | ||
<version>8.0.0.Final</version> | ||
<type>pom</type> | ||
<scope>import</scope> | ||
</dependency> | ||
|
||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-core</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-adapter-core</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
</dependencies> | ||
</dependencyManagement> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.jboss.spec.javax.servlet</groupId> | ||
<artifactId>jboss-servlet-api_3.1_spec</artifactId> | ||
<scope>provided</scope> | ||
</dependency> | ||
|
||
<!-- Contains KeycloakDeployment and KeycloakConfigResolver --> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-adapter-core</artifactId> | ||
</dependency> | ||
|
||
<!-- Contains KeycloakPrincipal --> | ||
<dependency> | ||
<groupId>org.keycloak</groupId> | ||
<artifactId>keycloak-core</artifactId> | ||
</dependency> | ||
</dependencies> | ||
<build> | ||
<finalName>${project.artifactId}</finalName> | ||
</build> | ||
</project> | ||
|
61 changes: 61 additions & 0 deletions
61
...ulti-tenant/src/main/java/org/keycloak/example/multitenant/boundary/ProtectedServlet.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors | ||
* as indicated by the @author tags. All rights reserved. | ||
* | ||
* 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.example.multitenant.boundary; | ||
|
||
import java.io.IOException; | ||
import java.io.PrintWriter; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.annotation.WebServlet; | ||
import javax.servlet.http.HttpServlet; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import org.keycloak.KeycloakPrincipal; | ||
|
||
/** | ||
* | ||
* @author Juraci Paixão Kröhling <juraci at kroehling.de> | ||
*/ | ||
@WebServlet(urlPatterns = "/*") | ||
public class ProtectedServlet extends HttpServlet { | ||
|
||
@Override | ||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { | ||
String realm = req.getPathInfo().split("/")[1]; | ||
if (realm.contains("?")) { | ||
realm = realm.split("\\?")[0]; | ||
} | ||
|
||
if (req.getPathInfo().contains("logout")) { | ||
req.logout(); | ||
resp.sendRedirect(req.getContextPath() + "/" + realm); | ||
return; | ||
} | ||
|
||
KeycloakPrincipal principal = (KeycloakPrincipal) req.getUserPrincipal(); | ||
|
||
resp.setContentType("text/html"); | ||
PrintWriter writer = resp.getWriter(); | ||
|
||
writer.write("Realm: "); | ||
writer.write(principal.getKeycloakSecurityContext().getIdToken().getIssuer()); | ||
|
||
writer.write("<br/>User: "); | ||
writer.write(principal.getKeycloakSecurityContext().getIdToken().getPreferredUsername()); | ||
|
||
writer.write(String.format("<br/><a href=\"/multitenant/%s/logout\">Logout</a>", realm)); | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
...c/main/java/org/keycloak/example/multitenant/control/PathBasedKeycloakConfigResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors | ||
* as indicated by the @author tags. All rights reserved. | ||
* | ||
* 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.example.multitenant.control; | ||
|
||
import java.io.InputStream; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
import org.keycloak.adapters.HttpFacade; | ||
import org.keycloak.adapters.KeycloakConfigResolver; | ||
import org.keycloak.adapters.KeycloakDeployment; | ||
import org.keycloak.adapters.KeycloakDeploymentBuilder; | ||
|
||
/** | ||
* | ||
* @author Juraci Paixão Kröhling <juraci at kroehling.de> | ||
*/ | ||
public class PathBasedKeycloakConfigResolver implements KeycloakConfigResolver { | ||
|
||
private final Map<String, KeycloakDeployment> cache = new ConcurrentHashMap<String, KeycloakDeployment>(); | ||
|
||
@Override | ||
public KeycloakDeployment resolve(HttpFacade.Request request) { | ||
String path = request.getURI(); | ||
String realm = path.substring(path.indexOf("multitenant/")).split("/")[1]; | ||
if (realm.contains("?")) { | ||
realm = realm.split("\\?")[0]; | ||
} | ||
|
||
KeycloakDeployment deployment = cache.get(realm); | ||
if (null == deployment) { | ||
// not found on the simple cache, try to load it from the file system | ||
InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak.json"); | ||
deployment = KeycloakDeploymentBuilder.build(is); | ||
cache.put(realm, deployment); | ||
} | ||
|
||
return deployment; | ||
} | ||
|
||
} |
Oops, something went wrong.