Setting up Keycloak for Securing Spring Boot Rest APIs

Overview

Keycloak is an open-source identity and access management solution which makes it easy to secure modern applications and services with little to no code.

The primary focus of this article is to set up Keycloak and secure Spring Boot REST APIs with Keycloak Spring Boot Adaptor.


Keycloak Deployment

There are many ways to deploy Keycloak.
  • Standalone Deployment with Keycloak Distribution Files
  • Standalone Deployment with Docker
  • High Availability Deployment in Kubernetes
In this article let’s deploy using Standalone Deployment with Keycloak Distribution Files.

Standalone Deployment with Keycloak Distribution Files

1. Make sure you have Java 11 installed to run the latest version of Keycloak.
If you are using a MacOS, HomeBrew is the easiest way to install Java 11. Otherwise, download Java 11 distribution based on the operating system.

brew cask install java@11

2. Download the latest Keycloak distribution from Keycloak GitHub Releases page. https://github.com/keycloak/keycloak/releases

3. Extract the downloaded Keycloak zip/tar.gz file.




Let’s examine the purpose of some of the directories:

bin: This contains various scripts to either boot the server or performs some other management action on the server.


Keycloak Guide

To get help configuring Keycloak via the CLI, run:

4. Open the Terminal and go to the location of bin -> D:\keycloak-18\keycloak-18.0.0\keycloak-18.0.0\bin

on Linux/Unix:

    $ bin/kc.sh

on Windows:

    $ bin\kc.bat

To try Keycloak out in development mode, run: 

on Linux/Unix:

    $ bin/kc.sh start-dev

on Windows:

    $ bin\kc.bat start-dev

After the server boots, open http://localhost:8080 in your web browser. The welcome page will indicate that the server is running.

To get started, check out the [configuration guides].

I'm using On Windows:

  • In CMD run:  $ bin\kc.bat

D:\keycloak-18\keycloak-18.0.0\keycloak-18.0.0\bin>kc.bat
Keycloak - Open Source Identity and Access Management

Find more information at: https://www.keycloak.org/docs/latest

Usage:

kc.bat [OPTIONS] [COMMAND]

Use this command-line tool to manage your Keycloak cluster.
Make sure the command is available on your "PATH" or prefix it with "./" (e.g.:
"./kc.bat") to execute from the current folder.

Options:

-cf, --config-file <file>
                     Set the path to a configuration file. By default, configuration properties are
                       read from the "keycloak.conf" file in the "conf" directory.
-h, --help           This help message.
-v, --verbose        Print out error details when running this command.
-V, --version        Show version information

Commands:

  build                   Creates a new and optimized server image.
  start                   Start the server.
  start-dev               Start the server in development mode.
  export                  Export data from realms to a file or directory.
  import                  Import data from a directory or a file.
  show-config             Print out the current configuration.
  tools                   Utilities for use and interaction with the server.
    completion            Generate bash/zsh completion script for kc.bat.

Examples:

  Start the server in development mode for local development or testing:

      $ kc.bat start-dev

  Building an optimized server runtime:

      $ kc.bat build <OPTIONS>

  Start the server in production mode:

      $ kc.bat start <OPTIONS>

  Enable auto-completion to bash/zsh:

      $ source <(kc.bat tools completion)

  Please, take a look at the documentation for more details before deploying in
production.

Use "kc.bat start --help" for the available options when starting the server.
Use "kc.bat <command> --help" for more information about other commands.




  • In CMD run:  $ bin\kc.bat start-dev
D:\keycloak-18\keycloak-18.0.0\keycloak-18.0.0\bin>kc.bat start-dev
Updating the configuration and installing your custom providers, if any. Please wait.
2022-05-05 13:38:26,731 INFO  [io.quarkus.deployment.QuarkusAugmentor] (main) Quarkus augmentation completed in 8614ms
2022-05-05 13:38:30,749 INFO  [org.keycloak.quarkus.runtime.hostname.DefaultHostnameProvider] (main) Hostname settings: FrontEnd: <request>, Strict HTTPS: false, Path: <request>, Strict BackChannel: false, Admin: <request>, Port: -1, Proxied: false
2022-05-05 13:38:32,452 WARN  [org.infinispan.PERSISTENCE] (keycloak-cache-init) ISPN000554: jboss-marshalling is deprecated and planned for removal
2022-05-05 13:38:32,670 WARN  [org.infinispan.CONFIG] (keycloak-cache-init) ISPN000569: Unable to persist Infinispan internal caches as no global state enabled
2022-05-05 13:38:32,714 INFO  [org.infinispan.CONTAINER] (keycloak-cache-init) ISPN000556: Starting user marshaller 'org.infinispan.jboss.marshalling.core.JBossUserMarshaller'
2022-05-05 13:38:33,017 INFO  [org.infinispan.CONTAINER] (keycloak-cache-init) ISPN000128: Infinispan version: Infinispan 'Triskaidekaphobia' 13.0.8.Final
2022-05-05 13:38:34,867 INFO  [org.keycloak.quarkus.runtime.storage.database.liquibase.QuarkusJpaUpdaterProvider] (main) Initializing database schema. Using changelog META-INF/jpa-changelog-master.xml
2022-05-05 13:38:38,856 INFO  [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: node_251114, Site name: null
2022-05-05 13:38:39,083 INFO  [org.keycloak.services] (main) KC-SERVICES0050: Initializing master realm
2022-05-05 13:38:41,762 INFO  [io.quarkus] (main) Keycloak 18.0.0 on JVM (powered by Quarkus 2.7.5.Final) started in 14.406s. Listening on: http://0.0.0.0:8080
2022-05-05 13:38:41,789 INFO  [io.quarkus] (main) Profile dev activated.
2022-05-05 13:38:41,790 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, jdbc-mariadb, jdbc-mssql, jdbc-mysql, jdbc-oracle, jdbc-postgresql, keycloak, narayana-jta, reactive-routes, resteasy, resteasy-jackson, smallrye-context-propagation, smallrye-health, smallrye-metrics, vault, vertx]
2022-05-05 13:38:41,799 WARN  [org.keycloak.quarkus.runtime.KeycloakMain] (main) Running the server in development mode. DO NOT use this configuration in production.


5. Access Keycloak using http://localhost:8080 with your favorite web browser.



6. Create an initial admin user by providing Username, Password, Password Confirmation, and clicking on ‘Create’ button.




You will receive the admin user creation confirmation as below.



7. Click on the Administration Console link to access Keycloak admin console. Provide the admin user credential used in the previous step and click on ‘Log In’.





8. Now you are connected to the Keycloak Admin Console.




9. Keycloak Server can be stopped by simply closing the Terminal window.


Keycloak Configuration

First, let’s make the required configurations in Keycloak.

Create Realm

A Realm manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.

  1. Go to http://localhost:8080/admin and log in to the Keycloak Admin Console using the admin credentials.
  2. From the Master drop-down menu, click Add Realm. When you are logged in to the master realm this drop-down menu lists all existing realms.
  3. Type Demo-Realm in the Name field and click Create.




When the realm is created, the main admin console page opens. Notice the current realm is now set to Demo-Realm. Switch between managing the master realm and the realm you just created by clicking entries in the Select realm drop-down menu.

Make sure Demo-Realm is selected for the below configurations. Avoid using the master realm. You don’t have to create the realm every time. It’s a one time process.


Create a Client

Clients are entities that can request Keycloak to authenticate a user. Most often, clients are applications and services that want to use Keycloak to secure themselves and provide a single sign-on solution. Clients can also be entities that just want to request identity information or an access token so that they can securely invoke other services on the network that are secured by Keycloak.

1. Click on the Clients menu from the left pane. All the available clients for the selected Realm will get listed here.



2. To create a new client, click Create. You will be prompted for a Client ID, a Client Protocol and a Root URL. A good choice for the client ID is the name of your application (springboot-microservice), the client protocol should be set to openid-connectand the root URL should be set to the application URL.




3. After saving you will be presented with the client configuration page where you can assign a name and description to the client if desired.

Set the Access Type to confidential, Authorization Enabled to ON , Service Account Enabled to ON and click Save.





Configure client with Access Type: ‘confidential’

Credentials tab will show the Client Secret which is required for the Spring Boot Application Keycloak configurations.





4. Go to Client Roles tab to create the spring boot-microservice role definitions. Imagine the Application that you are building with have different types of users with different user permissions. Ex: users and administrators.

  • Some APIs would only be accessible to users only.
  • Some APIs would be accessible to administrators only.
  • Some APIs would be accessible to both users and administrators.
As per the example, let’s create two roles: user and admin by clicking Add Role button.




Add ‘user’ role and Save




Add ‘admin’ role and Save




‘springboot-microservice’ Client Roles after adding ‘user’, ‘admin’ roles


Create Realm Roles

Applications often assign access and permissions to specific roles rather than individual users as dealing with users can be too fine grained and hard to manage.

Let’s create app-user and app-admin Realm roles by assigning corresponding springboot-microservice roles (user, admin).

1. Click on the Roles menu from the left pane. All the available roles for the selected Realm will get listed here.

Realm Roles in Keycloak Admin Console



2. To createapp-user realm role, click Add Role. You will be prompted for a Role Name, and a Description. Provide the details as below and Save.


Adding ‘app-user’ Realm Role

After Save, enabled Composite Roles and Search for springboot-microservice under Client Roles field. Select user role of the springboot-microservice and Click Add Selected >.

Assign ‘user’ Client Role to ‘app-user’ Realm Role

This configuration will assign springboot-microservice user client role to the app-user realm role. If you have multiple clients with multiple roles, pick and choose the required roles from each client to create realm roles based on the need.

3. Follow the same steps to create the app-admin user but assign admin client role instead of user role.

Assign ‘admin’ Client Role to ‘app-admin’ Realm Role


Create Users

Users are entities that are able to log into your system. They can have attributes associated with themselves like email, username, address, phone number, and birth day. They can be assigned group membership and have specific roles assigned to them.



Let’s create following users and grant them app-user and app-admin roles for testing purposes.
  • employee1 with app-userrealm role
  • employee2 with app-adminrealm role
  • employee3 with app-user & app-adminrealm roles
  1. From the menu, click Users to open the user list page.
  2. On the right side of the empty user list, click Add User to open the add user page.
  3. Enter a name in the Username field; this is the only required field. Flip the Email Verified switch from Off to On and click Save to save the data and open the management page for the new user.

Add New User to ‘Demo-Realm‘



4. Click the Credentials tab to set a temporary password for the new user.

5. Type a new password and confirm it. Flip the Temporary switch from On to Off and click Reset Password to set the user password to the new one you specified. For simplicity let’s set the password to mypassword for all the users.


Setting Credentials to Users



6. Click the Role Mappings tab to assign realm roles to the user. Realm roles list will be available in Available Roles list. Select one required role and click on the Add Selected > to assign it to the user.

After role assignment, assigned roles will be available under Assigned Roles list. Role assignments for employee1, employee2, and employee3 would be as below.


`employee1` Role Assignment


`employee2` Role Assignment


`employee3` Role Assignment



Yes, it was a bit of a hassle to go through all the configurations. But when you keep using Keycloak, these configurations will become a piece of cake. For new microservices getting added, you don’t need to do all of the above. You just need to add a new client with client roles and assign the client roles to corresponding realm roles.


Generate Tokens

Let’s learn how to generate an access token for Keycloak users.

1. Go to Realm Settings of the Demo-Realm from the left menu and click on OpenID Endpoint Configuration to view OpenID Endpoint details.


Realm Settings of ‘Demo-Realm’



2. Copy token_endpoint from the OpenID Endpoint Configuration. URL would look like:

http://localhost:8080/realms/Demo-Realm/protocol/openid-connect/token

3. Use the following CURL command to generate user credentials. Replace KEYCLOAK_SERVER_URL, REALM_NAME, CLIENT_ID, CLIENT_SECRET, USERNAME, PASSWORD with correct values.

curl -X POST
'<KEYCLOAK_SERVER_URL>/auth/realms/
REALM_NAME>/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=<CLIENT_ID>' \
--data-urlencode 'client_secret=<CLIENT_SECRET>' \
--data-urlencode 'username=<USERNAME>' \
--data-urlencode 'password=<PASSWORD>'

Example:

curl -X POST
'http://localhost:8080/auth/realms/
Demo-Realm/protocol/openid-connect/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=password' \
  --data-urlencode 'client_id=springboot-microservice' \
  --data-urlencode 'client_secret=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx' \
  --data-urlencode 'username=employee1' \
  --data-urlencode 'password=mypassword'

Execute the CURL from Terminal or use Postman. The response would look like below.





Spring Boot Application Configuration

Let’s build a new Spring Boot application and configure it with Keycloak Spring Boot Adaptor.

Creating the Spring Boot Application

To set up the Spring Boot Application go through my previous article Spring Boot CRUD with MongoDB, Postman For Starters | by Arkam_ahamed | Dec, 2021 | Medium

Do not forget to add these Dependencies: Add Spring Web, Spring Security and Spring Boot DevTools

pom.xml Changes

Open pom.xml and make the following changes

1. Add Keycloak Version Property

Find the <properties> section and add <keycloak.version> property. Property values should match with the Keycloak version. In my case 9.0.2.

<properties>
<java.version>11</java.version>
<keycloak.version>9.0.2</keycloak.version>
</properties>


2. Add Keycloak Dependency

Find the <dependencies> section and add keycloak-spring-boot-starter.

<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>${keycloak.version}</version>
</dependency>
...
</dependencies>

3. Add Dependency Management

Below the <dependencies> section add below section.

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

Check the updated pom.xml changes here.

application.properties

Open application.properties under src/main/resources and provide required Keycloak Configurations.

server.port = 8000keycloak.realm = <REALM_NAME>
keycloak.auth-server-url = <KEYCLOAK_SERVER_URL>/auth
keycloak.ssl-required = external
keycloak.resource = <CLIENT_ID>
keycloak.credentials.secret = <CLIENT_SECRET>
keycloak.use-resource-role-mappings = true
keycloak.bearer-only = true

Example

server.port = 8000keycloak.realm = Demo-Realm
keycloak.auth-server-url = http://localhost:8080/auth
keycloak.ssl-required = external
keycloak.resource = springboot-microservice
keycloak.credentials.secret = XXXXXXXXXXXXXXXXXXXXXXXXX
keycloak.use-resource-role-mappings = true
keycloak.bearer-only = true


KeycloakSecurityConfig.java

Keycloak provides a KeycloakWebSecurityConfigurerAdapter as a convenient base class for creating a WebSecurityConfigurer instance. The implementation allows customization by overriding methods. While its use is not required, it greatly simplifies your security context configuration.

Let’s create KeycloakSecurityConfig.java in config package.


configureGlobal: Registers the KeycloakAuthenticationProvider with the authentication manager.

sessionAuthenticationStrategy: Defines the session authentication strategy.

KeycloakConfigResolver : By Default, the Spring Security Adapter looks for a keycloak.json configuration file. You can make sure it looks at the configuration provided by the Spring Boot Adapter by adding this bean

@EnableGlobalMethodSecurity: The jsr250Enabled property allows us to use the @RoleAllowed annotation. We’ll explore more about this annotation in the next section.


TestController.java

We need some dummy APIs to test the API security.

Create TestController.java in controller package.


Run the Spring Boot Application. Make sure Maven is installed and configured.

mvn spring-boot:run






Define Role-Based Access with @RolesAllowed Annotation

Use @RolesAllowed annotation can be used to define the allowed user roles.

/test/anonymous:

This API should be accessible without any Authorization token with no restrictions. It already meets our requirements and needs no additional changes.

/test/user:

This API should be accessible to users with springboot-microservice user role. This can be defined by changing the code as below.

@RolesAllowed("user")
@RequestMapping(value = "/user", method = RequestMethod.GET)
public ResponseEntity<String> getUser(@RequestHeader String Authorization) {
return ResponseEntity.ok("Hello User");
}


/test/admin:

This API should be accessible to users with springboot-microservice admin role. This can be defined by changing the code as below.

@RolesAllowed("admin")
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public ResponseEntity<String> getAdmin(@RequestHeader String Authorization) {
return ResponseEntity.ok("Hello Admin");
}


/test/all-user:

This API should be accessible to users with springboot-microservice user & admin roles. This can be defined by changing the code as below.

@RolesAllowed({ "admin", "user" })
@RequestMapping(value = "/all-user", method = RequestMethod.GET)
public ResponseEntity<String> getAllUser(@RequestHeader String Authorization) {
return ResponseEntity.ok("Hello All User");
}

Now TestController would look like below.


Restart the Spring Boot Application and test above APIs by passing tokens from employee1, employee2, employee3 access tokens in the Authorization header with the bearer prefix (bearer <ACCESS_TOKEN>).

curl -X GET 'http://localhost:8000/test/user' \
--header 'Authorization: bearer <ACCESS_TOKEN>'Outputs:
anonymous: 403 Forbidden
employee1: Hello User
employee2: 403 Forbidden
employee3: Hello Usercurl -X GET 'http://localhost:8000/test/admin' \
--header 'Authorization: bearer <ACCESS_TOKEN>'Outputs:
anonymous: 403 Forbidden
employee1: 403 Forbidden
employee2: Hello Admin
employee3: Hello Admincurl -X GET 'http://localhost:8000/test/all-user' \
--header 'Authorization: bearer <ACCESS_TOKEN>'Outputs:
anonymous: 403 Forbidden
employee1: Hello All User
employee2: Hello All User
employee3: Hello All User

If the token is expired, you will receive 401 Unauthorized error.

Define Role-Based Access with Security Configuration

Rather than using @RolesAllowed annotation, the same configuration can be made in KeycloakSecurityConfig class as below.


That's it!
Hope this article can help!.

Source reference: 
https://systemweakness.com/setting-up-keycloak-and-securing-spring-boot-rest-apis-1765a85f5ac4
https://medium.com/devops-dudes/securing-spring-boot-rest-apis-with-keycloak-1d760b2004e



Previous Post Next Post