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
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.
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
Let’s examine the purpose of some of the directories:
Keycloak Guide
To get help configuring Keycloak via the CLI, run:
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.
You will receive the admin user creation confirmation as below.
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.
- Go to http://localhost:8080/admin and log in to the Keycloak Admin Console using the admin credentials.
- 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.
- 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.
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.
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.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.
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
- From the menu, click Users to open the user list page.
- On the right side of the empty user list, click Add User to open the add user page.
- 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.
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.
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>'
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'
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 | Mediumpom.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.
2. Add Keycloak Dependency
Find the <dependencies> section and add keycloak-spring-boot-starter.
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>
<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>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>${keycloak.version}</version>
</dependency>
...
</dependencies>
<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>
<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>
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
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
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
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.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
TestController.java
We need some dummy APIs to test the API security.
Create TestController.java in controller package.
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:
@RolesAllowed("user")
@RequestMapping(value = "/user", method = RequestMethod.GET)
public ResponseEntity<String> getUser(@RequestHeader String Authorization) {
return ResponseEntity.ok("Hello 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");
}
@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");
}
@RequestMapping(value = "/all-user", method = RequestMethod.GET)
public ResponseEntity<String> getAllUser(@RequestHeader String Authorization) {
return ResponseEntity.ok("Hello All User");
}
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
--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
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
Related Article:
- How to Customize Java HasMap RowMapper Dynamically covert from Selected SQL Query to Jason Data as API response
-
How to customize logging and configure info, debug, error in spring boot
maven project- Spring Boot REST + JPA + Hibernate + MySQL - Base Configuration