Skip to content

paulojeronimo/keycloak-spring-boot-tutorial

Repository files navigation

Keycloak tutorial to secure Spring Boot applications

Introduction

This document presents you a set of labs (and/ with references) related to Keycloak and UAA that you can do to increase your knowledge in these security tools.

The Keycloak labs here are configured to use the following versions: 3.4.3.Final (docs) and 4.8.3.Final (docs).

The UAA labs are configured to use the following versions: 4.28.0.

These labs were created and tested in a macOS environment. But, it can be easily adapted to run in other environments like Linux or Windows (with some Bash support).

Lab A: Setup the JDK local development environment

Note
sdk (SDKMAN) is a command line too used in this tutorial to set up the JDK version used. To proceed the next steps you must install it.

List available JDK versions:

$ sdk list java

Install JDK 8:

$ sdk install java 8u161-oracle

Install JDK 11:

$ sdk install java 11.0.2-open

Check the version:

$ java -version

Set default JDK to version 8:

$ sdk default java 8u161-oracle
$ java -version

Lab B: Keycloak versions download and install

Lab B1: Keycloak download

Note

You can skip the following steps if you want to build keycloak. After that, return to Lab B2.

Download the versions of Keycloak used in this tutorial:

$ wget -c https://downloads.jboss.org/keycloak/3.4.3.Final/keycloak-3.4.3.Final.tar.gz
$ wget -c https://downloads.jboss.org/keycloak/4.8.3.Final/keycloak-4.8.3.Final.tar.gz

Lab B2: Keycloak install

Extract both Keycloak versions:

$ tar xvfz keycloak-3.4.3.Final.tar.gz
$ tar xvfz keycloak-4.8.3.Final.tar.gz

Let’s Source the file scripts/bashrc (and include it in ~/.bash_profile). We’ll use this file to define some variables and functions simplifying our life!

$ source $PWD/scripts/bashrc
$ echo "!!" >> ~/.bash_profile

Lab C: Keycloak versions build

Note
This is an optional lab if you followed the steps in Lab B2. Skip directly to Lab D if you dont’t want details about how to build Keycloak.

Download Keycloak source code:

$ git clone https://github.com/keycloak/keycloak
$ cd keycloak

Check the last 2 versions available for Keycloak version 3:

$ git tag | grep '^3.*Final' | tail -2

Compile version 3.4.3.Final:

$ git checkout 3.4.3.Final
$ mvn install -Pdistribution -DskipTests
Note
You’ll get errors if you try to compile 3.4.3.Final using JDK 11.0.2-open version. So, pay attention if you configured your current JDK version to 8u161-oracle version.

Check the built files:

$ ls -l distribution/server-dist/target/*.{tar.gz,zip}

Save the built distribution to KEYCLOAK_LAB:

$ cp distribution/server-dist/target/keycloak-3.4.3.Final.tar.gz ..
Note
Keycloak version 3.4.3.Final is used in RHSSO 7.2 (this means more stability).
Keycloak version 4.8.3.Final is currently used in RHSSO 7.3.

Check the last 2 versions available for Keycloak version 4:

$ git tag | grep '^4.*Final' | tail -2

Compile version 4.8.3.Final:

$ git checkout 4.8.3.Final
$ mvn -Pdistribution -pl distribution/server-dist -am -Dmaven.test.skip clean install
Note
As the same case as in Keycloak 3, if you try to compile Keycloak 4 using JDK version 11.0.2-open you will get errors.
$ ls -l distribution/server-dist/target/*.{tar.gz,zip}

Save the built distribution to KEYCLOAK_LAB:

$ cp distribution/server-dist/target/keycloak-4.8.3.Final.tar.gz ..

Go back to the KEYCLOAK_LAB directory:

$ cd ..

Follow the steps in Lab B2.

Lab D: Running keycloak

Run Keycloak:

$ keycloak-start

Open http://localhost:8180 and configure the user and password to access de Administration Console.

Lab E: Running quick start applications

Download keycloak-quickstarts

Leave Keycloak running and open another shell.

$ git clone https://github.com/keycloak/keycloak-quickstarts.git
$ cd keycloak-quickstarts
$ git checkout 4.8.3.Final
$ git apply ../patches/keycloack-quickstarts/4.8.3.Final/pom.xml

Running app-authz-rest-springboot

The app-authz-rest-springboot quickstart demonstrates how to protect a Spring Boot REST service using Keycloak Authorization Services.

This quickstart tries to focus on the authorization features provided by Keycloak Authorization Services, where resources are protected by a set of permissions and policies defined in Keycloak and access to these resources are enforced by a policy enforcer (PEP) that intercepts every single request sent to the application to check whether or not access should be granted.

In this application, there are three paths protected by specific permissions in Keycloak:

  • /api/{resource}, where access to this resource is based on the evaluation of permissions associated with a resource Default Resource in Keycloak. Basically, any user with a role user is allowed to access this resource. Examples of resource that match this path pattern are: /api/resourcea and /api/resourceb.

  • /api/premium, where access to this resource is based on the evaluation of permissions associated with a resource Premium Resource in Keycloak. Basically, only users with a role user-premium is allowed to access this resource.

  • /api/admin, where access to this path is based on the evaluation of permissions associated with a resource Admin Resource in Keycloak. Basically, any user can access this resource as long as a specific request parameter is set.

We can use two distinct users to access this application:

Username Password Roles

alice

alice

user

jdoe

jdoe

user, user-premium

Let’s change to the application directory:

$ cd app-authz-rest-springboot

We need to import the file config/quickstart-realm.json. To do this, click in Add realm button:

keycloak add realm

Then import the file by clicking on Select file button:

keycloak add realm select file
Note
We can click on View details to see more information about the realm that we are adding.

We need to click Save.

Now, let’s run the Spring Boot app:

$ mvn spring-boot:run

Backing to the dir $KEYCLOAK_LAB, let’s obtain the OAuth2 access token for user alice:

$ keycloak-lab
$ curl -X POST \
  http://localhost:8180/auth/realms/spring-boot-quickstart/protocol/openid-connect/token \
  -H 'Authorization: Basic YXBwLWF1dGh6LXJlc3Qtc3ByaW5nYm9vdDpzZWNyZXQ=' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'username=alice&password=alice&grant_type=password' \
  | jq -r .access_token > alice.access_token
Note
  1. The parameter Authorization is a base 64 encoded string which can be decoded as follows:

    $ echo 'YXBwLWF1dGh6LXJlc3Qtc3ByaW5nYm9vdDpzZWNyZXQ=' | base64 -D
    app-authz-rest-springboot:secret
  2. jq -r is used to get only the value for the access_token field in the returned JSON.

  3. The value of the field access_token will be inserted in a file with the corresponding name.

Let’s install a Node.js JSON Web Token (JWT) decoder (jwt-cli) in order to inspect the contents of the access_token:

$ npm install -g jwt-cli

Now let’s use it:

$ jwt $(cat alice.access_token)

By inspecting the contents of the alice.access_token we can see that it will be valid only for 5 min (fields iat and exp). If we try to use it again after this period, we will see an error appearing in the console of the Spring Boot Application:

ERROR 5729 --- [nio-8080-exec-6] o.k.a.BearerTokenRequestAuthenticator    : Failed to verify token

org.keycloak.exceptions.TokenNotActiveException: Token is not active

We can configure the value of Access Token Lifespan field if we want to increase this period.

keycloak access token config

So, let’s update the this max time to 10 minutes. After that, we run the following command to get he OAuth2 access token for the user jdoe:

$ curl -X POST \
  http://localhost:8180/auth/realms/spring-boot-quickstart/protocol/openid-connect/token \
  -H 'Authorization: Basic YXBwLWF1dGh6LXJlc3Qtc3ByaW5nYm9vdDpzZWNyZXQ=' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'username=jdoe&password=jdoe&grant_type=password' \
  | jq -r .access_token > jdoe.access_token

Now, we can check the value for the fields iat and exp for the received token using the following command:

$ jwt $(cat jdoe.access_token) | grep -e iat: -e exp:

Accessing Protected Resources using an OAuth2 Access Token

Let’s try access the api/resourcea using the token received for alice:

$ curl -v -X GET http://localhost:8080/api/resourcea -H "Authorization: Bearer $(cat alice.access_token)"

We expect the following response: Access Granted.

Note
  1. We can change resourcea to resourceb in the request and we can see the same response.

  2. The access to the resources /api/admin or /api/premium we will be denied.

Using the token received for jdoe we can also access the /api/premium resource:

$ curl -v -X GET http://localhost:8080/api/premium -H "Authorization: Bearer $(cat jdoe.access_token)"

Running app-authz-springboot

The app-authz-springboot quickstart demonstrates how to write a SpringBoot Web application where both authentication and authorization aspects are managed by Keycloak.

This application tries to focus on the authorization features provided by Keycloak Authorization Services, where resources are protected by a set of permissions and policies defined in Keycloak itself and access to these resources are enforced by a policy enforcer that intercepts every single request to the application.

In this application, there are three paths protected by specific permissions in Keycloak:

  • /protected, where access to this page is based on the evaluation of permissions associated with a resource Protected Resource in Keycloak. Basically, any user with a role user is allowed to access this page.

  • /protected/premium, where access to this page is based on the evaluation of permissions associated with a resource Premium Resource in Keycloak. Basically, only users with a role user-premium is allowed to access this page.

  • /protected/alice, where access to this page is based on the evaluation of permissions associated with a resource Alice Resource in Keycloak. Basically, only user alice is allowed to access this page.

The home page (home.ftl) also demonstrates how to use a AuthorizationContext instance to check for user`s permissions and hide/show things in a page. Where the AuthorizationContext encapsulates all permissions granted by a Keycloak server and provides methods to check these permissions.

We can use the same users registered in the previous lab with the same password and roles.

Configuration in Keycloak

We need to delete the previously configured realm: spring-boot-quickstart.

Then we need to recreate the realm:

  • In the top left corner dropdown menu that is titled Master, click Add Realm. If you are logged in to the master realm this dropdown menu lists all the realms created.

  • Click on Select File and import the file keycloak-quickstarts/app-authz-springboot/config/quickstart-realm.json.

  • Click Create.

Build and Run

First, stop the execution for the last lab (app-authz-rest-springboot) if it is already running!

Then, start the microservice for this lab:

$ cd $KEYCLOAK_LAB/keycloak-quickstarts/app-authz-springboot/
$ mvn spring-boot:run

Open http://localhost:8080. Test the app using the usernames provided (alice and jdoe).

Running service-springboot-rest

The service-springboot-rest quickstart demonstrates how to write a RESTful service with SpringBoot that is secured with Keycloak.

Start it by running the tests:

$ mvn test -Pspring-boot

Read ProductServiceTest.java in order to understanding how to test a Keycloak app.

Lab F: Developing Spring Boot applications integrated with Keycloak (from scratch)

Lab F1: Executing Sebastien Blanc tutorial

Read the article Easily secure your Spring Boot applications with Keycloak (and see the referenced videos: 1 and 2).

In order to simply run the code showed in this article you can do the following steps:

Stop previous running instances of Keycloak.

Configure keycloak instance to use 3.4.3.Final and start it:

$ keycloak-use 3
$ keycloak-start

Clone the project:

$ keycloak-lab
$ git clone https://github.com/paulojeronimo/spring-boot-keycloak-tutorial
$ cd spring-boot-keycloak-tutorial

Create a new realm on Keycloak by importing the file springdemo.json.

Run:

$ mvn spring-boot:run

Access http://localhost:8080/products (User: sebi, Password: sebi).

To switch to Spring Security version, stop (Ctrl+C) the running app and do the following commands:

$ git branch -a
$ git checkout remotes/origin/spring-security

Compare this branch with the master branch:

$ git difftool master...origin/spring-security

Run:

$ mvn clean spring-boot:run
Note

The GitHub repository sebastienblanc/spring-boot-keycloak-tutorial has some useful forks (some with more features added):

These most active forks were discovered by using this tool.

Lab F2: Creating a resource server (backend) using kcadm, gradle and spring-security

Stop previous Keycloak instances.

Configure your environment to use Keycloak 4.8.3.Final, reinstall and start it:

$ keycloak-use 4
$ keycloak-install
$ keycloak-start

Access http://localhost:8180 and create the admin with password admin.

Do the following steps:

Change to the labs dir:

$ keycloak-lab

Create the sample by using Spring Initializr:

$ rm -rf samples/keycloak-resource-server-demo/
$ mkdir -p samples/ && cd $_
$ curl https://start.spring.io/starter.tgz \
  -d bootVersion=2.1.3.RELEASE \
  -d dependencies=web,security \
  -d type=gradle-project \
  -d baseDir=keycloak-resource-server-demo \
  | tar -xzvf -

Do your first commit:

$ cd keycloak-resource-server-demo
$ git init
$ git add -A
$ git commit -m 'Initial commit'

Apply the following patch to configure keycloack support on build.gradle:

$ git apply ../../patches/keycloak-resource-server-demo/build.gradle.diff

Create REST endpoints that will be secured:

$ d=src/main/java/com/example/demo
$ cp ../../starts/keycloak-resource-server-demo/$d/HelloEndpoint.java $d/

Configure the Keycloak security:

$ cp ../../starts/keycloak-resource-server-demo/$d/KeycloakSecurityConfigurer.java $d/

Login into Keycloak as an administrator through the command line:

$ kcadm.sh config credentials --server http://localhost:8180/auth --realm master --user admin --password admin

Create a REALM:

$ kcadm.sh create realms -s realm=spring-security-example -s enabled=true

Create the clients:

$ CID1=$(kcadm.sh create clients -r spring-security-example -s clientId=curl -s enabled=true -s publicClient=true -s baseUrl=http://localhost:8080 -s adminUrl=http://localhost:8080 -s directAccessGrantsEnabled=true -i)
$ CID2=$(kcadm.sh create clients -r spring-security-example -s clientId=spring-security-demo-app -s enabled=true -s baseUrl=http://localhost:8080 -s bearerOnly=true -i)

Add some roles:

$ kcadm.sh create clients/$CID2/roles -r spring-security-example -s name=admin -s 'description=Admin role'
$ kcadm.sh create clients/$CID2/roles -r spring-security-example -s name=user -s 'description=User role'

Get the client configuration to know how to configure your application.properties:

$ kcadm.sh  get clients/$CID2/installation/providers/keycloak-oidc-keycloak-json -r spring-security-example
{
  "realm" : "spring-security-example",
  "bearer-only" : true,
  "auth-server-url" : "http://localhost:8180/auth",
  "ssl-required" : "external",
  "resource" : "spring-security-demo-app",
  "verify-token-audience" : true,
  "use-resource-role-mappings" : true,
  "confidential-port" : 0
}

Configure the application.properties:

$ cat > src/main/resources/application.properties <<'EOF'
keycloak.realm=spring-security-example
keycloak.bearer-only=true
keycloak.auth-server-url=http://localhost:8180/auth
keycloak.ssl-required=external
keycloak.resource=spring-security-demo-app
keycloak.use-resource-role-mappings=true
keycloak.confidential-port=0
EOF

Create the user joe_admin and some add roles:

$ joe=$(kcadm.sh create users -r spring-security-example -s username=joe_admin -s enabled=true -i)
$ kcadm.sh update users/$joe/reset-password -r spring-security-example -s type=password -s value=admin -s temporary=false -n
$ kcadm.sh add-roles -r spring-security-example --uusername=joe_admin --cclientid spring-security-demo-app --rolename admin

Create the user jim_user and some add roles:

$ jim=$(kcadm.sh create users -r spring-security-example -s username=jim_user -s enabled=true -i)
$ kcadm.sh update users/$jim/reset-password -r spring-security-example -s type=password -s value=admin -s temporary=false -n
$ kcadm.sh add-roles -r spring-security-example --uusername=jim_user --cclientid spring-security-demo-app --rolename user

Start the application:

$ ./gradlew bootRun

Test the application with user joe_admin:

$ export JOE_TOKEN=`curl -ss --data "grant_type=password&client_id=curl&username=joe_admin&password=admin" http://localhost:8180/auth/realms/spring-security-example/protocol/openid-connect/token | jq -r .access_token`

$ jwt $JOE_TOKEN

$ curl -H "Authorization: bearer $JOE_TOKEN" http://localhost:8080/admin/hello
Hello Admin

$ curl -H "Authorization: bearer $JOE_TOKEN" http://localhost:8080/user/hello
{"timestamp":1544591383960,"status":403,"error":"Forbidden","message":"Access is denied","path":"/user/hello"}

Test the application with user jim_user:

$ export JIM_TOKEN=`curl -ss --data "grant_type=password&client_id=curl&username=jim_user&password=admin" http://localhost:8180/auth/realms/spring-security-example/protocol/openid-connect/token | jq -r .access_token`

$ curl -H "Authorization: bearer $JIM_TOKEN" http://localhost:8080/admin/hello
{"timestamp":1544607993019,"status":403,"error":"Forbidden","message":"Access is denied","path":"/admin/hello"}

$ curl -H "Authorization: bearer $JIM_TOKEN" http://localhost:8080/user/hello
Hello User

Test the application without any user:

$ curl http://localhost:8080/guest/hello
Hello Guest

Do a commit:

$ git add -A
$ git commit -m 'Added Keycloak support'

Stop the Spring Boot application.

Modifiy your application with the following commands:

$ git apply ../../patches/keycloak-resource-server-demo/src/main/java/com/example/demo/HelloEndpoint.java.diff
$ git apply ../../patches/keycloak-resource-server-demo/src/main/resources/application.properties.diff

See what was changed:

$ git difftool

Restart the application:

$ ./gradlew bootRun

Test application againg:

$ export JOE_TOKEN=`curl -ss --data "grant_type=password&client_id=curl&username=joe_admin&password=admin" http://localhost:8180/auth/realms/spring-security-example/protocol/openid-connect/token | jq -r .access_token`
$ curl -H "Authorization: bearer $JOE_TOKEN" http://localhost:8080/admin/hello

Do another commit:

$ cat > README.adoc <<'EOF'
= keycloak-resource-server-demo

This application was created using the steps described in https://github.com/paulojeronimo/keycloak-spring-boot-tutorial#lab-f2[keycloak-spring-boot-tutorial].
EOF

$ git add -A
$ git commit -m 'Added support to show principal name'

Lab H: Migrating Spring Boot microservices security from UAA to Keycloak

Lab H1: Spring Boot microservices app secured by UAA

Setup the JDK to use version 8 (otherwise UAA will not compile on version 4.28.0):

$ sdk default java 8u161-oracle

Install and start the UAA server through the functions currently loaded (by scripts/bashrc) in your shell:

$ uaa-install
$ uaa-start

Open another shell and clone the oauth-uaa-sample into the samples dir:

$ keycloak-lab
$ mkdir -p samples && cd $_
$ git clone https://github.com/paulojeronimo/oauth-uaa-sample
$ cd oauth-uaa-sample

Follow the steps in oauth-uaa-sample/README.adoc to run the application.

Lab H2: Spring Boot 2 microservices app secured by UAA

Lab H3: Migrating security from UAA to Keycloak

Under construction …​

Lab I: Configuring Keycloak to use a PostgreSQL database

TODO

Lab J: Integrating Keycloak with LDAP

Lab K: Using Keycloak with SAML

TODO

Postman configuration samples

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published