Over a million developers have joined DZone.

Securing WildFly Swarm Apps With KeyCloak

Learn how to secure your WildFly Swarm apps using KeyCloak in this great tutorial, from the Swarm ecosystem to securing apps.

Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

Wildfly Swarm does a lot more than just packaging up your Java EE applications into a self-contained JAR. The Swarm ecosystem also includes customized versions of third party applications, like Swagger and KeyCloak.

Anyone who has been tasked with writing enterprise applications will soon find themselves leaning on solutions like these to efficiently integrate commodity, but crucial, functional requirements. I have found KeyCloak especially to be a lifesaver when SSO, social login and federation within existing user databases like Windows Active Directory are requested as if this is just a tick box. Implementing a custom solution is many months worth of work, but KeyCloak really does expose this functionality through a tick box.

By allowing a service like KeyCloak to be bundled up alongside your microservice, it is trivial for developers to deploy a self-contained and secure service that accommodate everything from locally defined users to federated SSO, all without any additional code in your apps.

Let’s take a look at what it takes to embed KeyCloak in a Swarm application, and then use it to secure our “hello world” JSF application.

Starting with the build script, we need to add dependencies on the Swarm KeyCloak server, client adapter and JBoss ShinkWrap. The KeyCloak server provides the centralized authentication services that our web application will defer to when a restricted resource is requested. The Keycloak client adapter provides the classes our app needs to interact with the KeyCloak server. ShinkWrap is an API that is used to build up Java artifacts like WAR files that can then be deployed as part of a Swarm Uberjar.

We also need to reference a class containing a custom main method, which is where we will make use of ShinkWrap to build up the WAR file.

Note that we have mixed Beta1 and Beta2 versions of Swarm dependencies here. This is the only way I could get a functioning deployment, and is something that I expect will be resolved when Swarm has a final release.

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
    }

    dependencies {
        classpath "org.wildfly.swarm:wildfly-swarm-plugin:1.0.0.Beta2"
    }
}

group 'com.matthewcasperson'
version '1.0-SNAPSHOT'

apply plugin: 'war'
apply plugin: 'wildfly-swarm'

swarm {
    mainClassName = "com.matthewcasperson.swarmdemo.MyMain"
}

sourceCompatibility = 1.8

def swarmVersionBeta2 = '1.0.0.Beta2'
def swarmVersionBeta1 = '1.0.0.Beta1'

repositories {
    mavenCentral()
    mavenLocal()
    maven {
        url 'http://repository.jboss.org/nexus/content/groups/public-jboss'
    }
    maven {
        url 'https://maven.repository.redhat.com/nexus/content/repositories/public'
    }
}

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
    compile 'org.wildfly.swarm:bootstrap:' + swarmVersionBeta2
    compile 'org.wildfly.swarm:jsf:' + swarmVersionBeta2
    compile 'org.wildfly.swarm:weld:' + swarmVersionBeta2
    compile 'org.wildfly.swarm:keycloak:' + swarmVersionBeta1
    compile 'org.wildfly.swarm:keycloak-server:' + swarmVersionBeta1
    compile 'org.jboss.shrinkwrap:shrinkwrap-api:1.2.3'
}

The custom main method makes use of the ShrinkWrap API to build a secured WAR archive.

package com.matthewcasperson.swarmdemo;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.wildfly.swarm.container.Container;
import org.wildfly.swarm.keycloak.Secured;
import org.wildfly.swarm.undertow.WARArchive;

/**
 * Swarm entry point
 */
public class MyMain {
    public static void main(final String... args) throws Exception {

        // Instantiate the container
        final Container container = new Container();

        // Create one or more deployments
        final WARArchive deployment = ShrinkWrap.create(WARArchive.class);

        deployment.as( Secured.class );

        container.start();
        container.deploy(deployment);
    }
}

The documentation explains that the code deployment.as( Secured.class ) will add support for KeyCloak, but itself will not actually secure any part of the application. The documentation goes on to detail how you can secure parts of your application, however, I found that the documented code didn’t work for a JSF application. I suspect it is tailored to JAX-RS REST-style applications. We will secure our JSF application via the web.xml file.

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <!-- Welcome page -->
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>

    <!-- JSF mapping -->
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Map these files with JSF -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.faces</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Admins</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

    <login-config>
        <auth-method>KEYCLOAK</auth-method>
        <realm-name>this is ignored currently</realm-name>
    </login-config>

    <security-role>
        <role-name>admin</role-name>
    </security-role>

</web-app>

The security code in the web.xml file comes straight from the KeyCloak documentation. We secure our JSF application as if we were contacting an external and separate instance of KeyCloak.

The last piece of the puzzle is the keycloak.json file. To get that, we need to boot our Swarm application (without our JSF application being fully configured yet), log into KeyCloak, and configure our security environment.

When the Uberjar is booted, point your browser to http://localhost:8080/auth. As this is the first time you have booted KeyCloak, you’ll be prompted to create a new KeyCloak admin user. This user is the one you will use to administer KeyCloak, and will not be used to log into applications protected by KeyCloak.

I created a user called “admin” with a password of “password”.

All configuration of KeyCloak is stored in an H2 database called keycloak.h2.db. This file will be included in the demo source code, though you would probably not include this file if you were distributing a production application.

Screen Shot 2016-03-25 at 2.05.15 PM.png

Applications are secured inside a realm, so we need to add a new realm to KeyCloak.

Screen Shot 2016-03-25 at 2.05.47 PM.png

I called my realm Swarm.

Screen Shot 2016-03-25 at 2.05.53 PM.png

Realms contain clients which in our case represent the application we are going to protect. I’ve created a client called SwarmDemo here.

Screen Shot 2016-03-25 at 2.06.12 PM.png

Screen Shot 2016-03-25 at 2.06.39 PM.png

Once a client is created, KeyCloak provides you with the contents of the keycloak.json file. This is then copied into src/main/webapp/WEB-INF.

Screen Shot 2016-03-25 at 2.06.55 PM.png

You will also need to define a role called admin and a user that you will log into your application with. You’ll find links to Roles and Users in the left-hand menu. Here I have created a role and two users. The user called “user” has the admin role, while the user called “guest” does not. Both have the password of “password”.

Screen Shot 2016-03-25 at 3.16.17 PM.png

Screen Shot 2016-03-25 at 3.16.39 PM.png

Once your Swarm application is recompiled with the keycloak.json file, opening http://localhost:8080 will redirect you to the KeyCloak login page. From here you enter the credentials that you defined for the users in the Swarm realm, and KeyCloak redirects you back to the web application.

Screen Shot 2016-03-25 at 3.19.40 PM.png

It might be hard to appreciate why KeyCloak is so useful from this simple example. Installing an authentication server for the sake of two locally defined user accounts may seem like overkill. But keep in mind that with just a small amount of configuration, your users could be logging in via their Google, Twitter, or Facebook accounts (KeyCloak supports quite a few different social logins), or they could be logging in via their Windows AD accounts. You get password reset functionality for free, security considerations have been baked in, and so much more.

The source code for this demo can be found on GitHub.

Learn tips and best practices for optimizing your capacity management strategy with the Market Guide for Capacity Management, brought to you in partnership with BMC.

Topics:
wildfly swarm ,keycloak ,performance ,security

Published at DZone with permission of Matthew Casperson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}