1. Introduction
Hello and welcome to the first article in a series showing how to use Keycloak with Spring Boot applications.
In this step by step guide, I will show you how to set up a Keycloak server and connect to it using Spring Boot and Kotlin. Additionally, you will learn how to secure REST endpoints with Keycloak combined with the @PreAuthorize annotation.
2. What is Keycloak?
According to the documentation, Keycloak is the Open Source Identity and Access Management solution. In simple terms, it allows us to add authentication and secure our applications with minimum effort. With Keycloak, plenty of topics, like user storage, authentication, and many many more, are available out of the box.
In addition, the keycloak-spring-boot-starter makes all the process of configuration really straightforward, thus saving our precious time.
3. Install Keycloak
As the first step in this tutorial will be the installation of the Keycloak server. There are plenty of ways to achieve that, and I highly recommend you visit the official documentation to choose the most suitable one for you.
To keep this tutorial as simple as possible, I’ve decided to deploy it as a Docker container. If you have the Docker environment already installed on your machine, all you need to do is to type the following command:
docker run -p 8090:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin -eย DB_VENDOR=h2 quay.io/keycloak/keycloak:11.0.3
With this command, we create a container using the keycloak:11.0.3 image and publish it’s 8080 port to the 8180 port of the host. Additionally, we pass the KEYCLOAK_USER and KEYCLOAK_PASSWORD environment variables for the initial admin user (root).
However, the simplicity comes with its own restrictions. By default, the Keycloak server runs using the H2 database, so all the configuration will be lost after the container is terminated. To prevent that we can export a realm configuration to the JSON file (I will show you this step later).
3. Configure Keycloak
Once the Keycloak server is up and running, let’s head to the administration console. All we need to do is to type http://localhost:8090 in the address bar of the browser. We should see the following page:
On the next page, we should see the login form. Let’s enter the credentials for the admin account we’ve already configured:
3.1. Create Realm
After we’ve authenticated successfully, we can create a new realm. In Keycloak, realms manage a set of users, credentials, roles, and groups. Each user belongs to and logs into some realm. Moreover, they are completely isolated environments, which introduce an additional layer of security to our application.
As the next step, let’s navigate to the Add realm button:
On the next page, let’s type the name for the new realm and hit the create button:
Please remember this name, we will need it to configure the Spring Boot application later.
3.2. Create Roles
Nextly, let’s create some roles for our future users. Let’s navigate to the Roles tab on the left sidebar. On the next screen, we should see the list of already existing roles. Let’s click the Add role button placed in the right part of the screen.
Firstly, let’s type the ROLE_USER and click save. After that, let’s add the ROLE_ADMIN which will be a composite role associated with the previous one:
Please notice two important things here:
- A composite role combines together multiple roles. To put it simply- a user with the ROLE_ADMIN will be able to access all endpoints restricted for ROLE_USER. It helps to clean the architecture of the security a lot, however, please be careful not to introduce an additional gap to the system when using it.
- Both roles have been prefixed with a ROLE_. Spring Security will automatically prefix hasRole checks with the same value.
3.3. Create Clients
As the next step, let’s create clients. Most often, Keycloak clients are the applications and services we would like to secure, or which are obtaining tokens to access other applications.
For the purpose of this tutorial, we will create two clients. Let’s start the one, we will use to configure our Spring Boot application:
Please notice, that we’ve set the Access Type to bearer-only. It means, that our application will not initiate a login, it’ll just pass received tokens to verify them with Keycloak. Most of the time, you will set this type of access type for the Spring Boot backends, which don’t need to call another microservices secured by Keycloak, or to use a service account.
Nextly, let’s create the public client, which will be used to get access tokens on behalf of the user:
This time, only the Direct Access Grants flow is enabled and the Access Type is set to public.
The public access type is used for clients, which cannot keep secrets securely, like frontend applications for instance.
3.4. Create Users
As the last step for Keycloak configuration, we will create users. Let’s head to the Users page, and create a user names example_user:
Let’s mark him enabled and his email verified as well.
The next important thing we need to do is to set the password for the user in the Credentials tab:
And as the last step, let’s add the desired role to the user. Let’s select the ROLE_USER and hit the Add selected button, just like below:
Please repeat the same steps for the admin user. The only difference will be the selection of the ROLE_ADMIN instead.
4. Configure Spring Boot App
4.1. Imports
As usual, we will start the configuration of our Spring Boot application with imports. Let’s head to the build.gradle.kts file and add the following dependencies:
implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.keycloak:keycloak-spring-boot-starter:11.0.3") implementation("org.springframework.boot:spring-boot-starter-security:2.4.0")
4.2. Configure Application Properties
As the next step, let’s open the /resources/application.yaml file and paste the following config:
keycloak: realm: example resource: spring-boot bearer-only: true auth-server-url: http://localhost:8090/auth
Let’s see what each of the above configs mean:
- realm– as the name suggests, it is the name of the created realm,
- resource– matches the name of the created client,
- bearer-only– this flag indicates, that our service has been created as a bearer-only, by default it is set to false,
- auth-server-url– it is the URL of our Keycloak server
4.3. Configure Spring Security
As the next step, let’s create the WebSecurityConfig class:
@KeycloakConfiguration @EnableGlobalMethodSecurity(prePostEnabled = true) class WebSecurityConfig : KeycloakWebSecurityConfigurerAdapter() { @Autowired fun configureGlobal(auth: AuthenticationManagerBuilder) { val keycloakAuthenticationProvider = keycloakAuthenticationProvider() keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(SimpleAuthorityMapper()) auth.authenticationProvider(keycloakAuthenticationProvider) } @Bean override fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy { return NullAuthenticatedSessionStrategy() } @Bean fun keycloakConfigResolver(): KeycloakConfigResolver { return KeycloakSpringBootConfigResolver() } override fun configure(http: HttpSecurity) { super.configure(http) http .authorizeRequests() .antMatchers("/api/public/**").permitAll() .anyRequest().fullyAuthenticated() } }
I totally understand that this config might be a little overwhelming, so let’s just break it down:
@KeycloakConfiguration @EnableGlobalMethodSecurity(prePostEnabled = true) class WebSecurityConfig : KeycloakWebSecurityConfigurerAdapter()
@KeycloakConfiguration annotation provides a Keycloak based Spring security configuration. It is used with classes extending KeycloakWebSecurityConfigurerAdapter, just like in our case. The @EnableGlobalMethodSecurity with prePostEnabledย set to true enables the Spring Security global method security and @PreAuthorize annotations.
@Autowired fun configureGlobal(auth: AuthenticationManagerBuilder) { val keycloakAuthenticationProvider = keycloakAuthenticationProvider() keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(SimpleAuthorityMapper()) auth.authenticationProvider(keycloakAuthenticationProvider) }
The configureGlobal function takes the AuthenticationManagerBuilder bean as a parameter and sets the KeycloakAuthenticationProvider as the authentication provider. Additionally, it sets the SimpleAuthorityMapper as the authority mapper.
@Bean override fun sessionAuthenticationStrategy(): SessionAuthenticationStrategy { return NullAuthenticatedSessionStrategy() } @Bean fun keycloakConfigResolver(): KeycloakConfigResolver { return KeycloakSpringBootConfigResolver() }
With these two functions, we declare the KeycloakSpringBootConfigResolver bean and that we do not want the session to be created, as well.
override fun configure(http: HttpSecurity) { super.configure(http) http .authorizeRequests() .antMatchers("/api/public/**").permitAll() .anyRequest().fullyAuthenticated() }
Finally, we override the configure method derived from the KeycloakWebSecurityConfigurerAdapter. With this config, all requests except the /api/public/** need to be fully authenticated.
5. Create Rest Controller
With all the above being done, we can create an ExampleController class, which consists of three endpoints:
@RestController @RequestMapping("/api") class ExampleController { @GetMapping("/example") @PreAuthorize("hasRole('USER')") fun getUserInfo(): ResponseEntity<String> = ResponseEntity.ok("User info") @GetMapping("/example/admin") @PreAuthorize("hasRole('ADMIN')") fun getAdminInfo(): ResponseEntity<String> = ResponseEntity.ok("Admin info") @GetMapping("/public/example") fun getPublicInfo(): ResponseEntity<String> = ResponseEntity.ok("Public info") }
The first two of them require the user to either have the ROLE_USER or ROLE_ADMIN assigned to him. Nevertheless, the last endpoint permits anyone, even without the token.
6. Testing with cURL
Finally, we can validate if everything is working as expected.
Let’s start by obtaining the access token:
curl --location --request POST 'http://localhost:8090/auth/realms/example/protocol/openid-connect/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'username=some_user' \ --data-urlencode 'password=password' \ --data-urlencode 'grant_type=password' \ --data-urlencode 'client_id=public'
As the result we should get the following JSON object:
{ "access_token": [TOKEN], "expires_in": 300, "refresh_expires_in": 1800, "refresh_token": [TOKEN], "token_type": "bearer", "not-before-policy": 0, "session_state": "7981f2e1-4e50-44d6-ac81-2ba508a59680", "scope": "email profile" }
The most important part of the response for us is the JWT access token, which we will use to query our endpoints. If you would like to see, how many informations are encoded within it, I highly recommend to visit jwt.io and check it out.
To fully validate our endpoints, let’s query all of them with different tokens and see if results correspond to our assumptions:
curl --location --request GET 'http://localhost:8080/api/example' \ --header 'Authorization: Bearer [TOKEN]' ## Result for both admin and user: User info curl --location --request GET 'http://localhost:8080/api/example/admin' \ --header 'Authorization: Bearer [TOKEN]' ## Result only for admin: Admin info curl --location --request GET 'http://localhost:8080/api/public/example' ## Result for any request: Public info
7. Export Realm Configuration
In the beginning, I’ve promised to show you how to export the realm configuration. The only thing we need to do is to click the Export tab on the left sidebar and nextly the Export button on the next page:
As a result, a JSON file will be generated.
If we would like to import the configuration, all we need is to head to the Import section:
8. Conclusion
And that would be all for this article. We’ve covered step by step, how to set up the Keycloak server with Spring Boot and Kotlin. Moreover, we’ve had a chance to see how to use it to secure Spring Boot REST endpoints with @PreAuthorize.
As I have written in the beginning, this is the first article in a series. If you would like me to cover some special aspects of the Keycloak/Spring Boot integration, please let me know in the comments, or by the contact form.
Also, to see the fully working project, please refer to this GitHub repository.
3 Responses
This useful example would be even more valuable with some additional information and corrections.
The docker keycloak invocation doesn’t work. It should be:
docker run -it -p 8080:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=password -e DB_VENDOR=h2 quay.io/keycloak/keycloak:12.0.2
Also, where should the WebSecurityConfig class be placed?
Thank you.
Hi Kurt, glad to meet you. You’re right, forgot that I’ve had the DB running in the background. When it comes to the class – I put it inside the config package. Please see the point 8- there is the link to the repo of this project
Hi Piotr
Thanks again for your valuable tutorial.
Do you have any advice about including the access token when performing a POST operation? It seems that simply adding the same header used in the GET curl examples does not seem to work with POST.
Thank you very much for any guidance that you may provide. ๐
Kurt