Secure your Spring Boot REST API with Keycloak

🕓 40 minutes

What you’ll learn:

  • How to add security to your REST API built on SpringBoot.
  • How to install a Keycloak managed service in CodeNow.
  • How to configure the Keycloak basics.
  • Basic concepts of authorization and authentication using Keycloak, OAUTH2.

Project Source

This sample project can be cloned from: http://gitlab.factory.codenow-control.codenow.com/public-docs/spring-boot-rest-api-secured-by-keycloak-example.git

Prerequisites

  • You are already familiar with CodeNow basics.
  • You have successfully built a REST API application component with Spring Boot scaffolder. See the How To Create Simple Spring Boot REST API tutorial.
  • You have already set up the project in your local development environment and imported into your IDE.

Keycloak installation and setup

  1. Navigate to "Marketplace / Get New Service" in CodeNow.

  2. Select "Add" next to Keycloak service under "Available Services".

  3. Enter the unique name of your instance and select target box + environment. Confirm creation.

  4. Once your service appears under "My Services" and the status is "Ready", open the details.

  5. Open the Keycloak admin panel, use the admin credentials provided under your service details to log in.

  6. Create a new realm by clicking at the "Add realm" button. It appears in the popup menu at the top of the left navigation pane. Confirm and leave it with defaults. Note: think of realm as a "security domain" for your future applications.

    keycloakAddRealm

  7. Click "Clients" and create a new client. The term "client" here refers to our application, so name it accordingly (for example: api). After creation, fill in the mandatory field "Valid Redirect URIs" (you can enter any value, we will not be using it in this tutorial). Leave the other settings to defaults and save your changes.

  8. Add a new role in the menu "Roles / Add Role", name it for example "api-role".

  9. Navigate to "Users / Add User" and create a new user, name it for example "api-user". Now go to the "Credentials" tab and enter a new password, uncheck "Temporary" switch and click "Set Password". Next, select "Role Mappings" and assign our role (api-role) to this user.

  10. Last thing we need to do is to write down our authorization endpoint. We will need it later in our application. It is composed of the base URL of your Keycloak instance + "/auth". For example https://mfr-keycloak-keycloak.box.codenow-dev.codenow.com/auth

We are done with the basic Keycloak setup.

Securing our SpringBoot application

  1. Add the following dependencies to pom.xml

    01 <dependency>
    02 <groupId>org.springframework.boot</groupId>
    03 <artifactId>spring-boot-starter-security</artifactId>
    04 </dependency>
    05 <dependency>
    06 <groupId>org.keycloak</groupId>
    07 <artifactId>keycloak-spring-boot-starter</artifactId>
    08 </dependency>
    09 <dependency>
    10 <groupId>org.keycloak</groupId>
    11 <artifactId>keycloak-spring-security-adapter</artifactId>
    12 <version>10.0.0</version>
    13 </dependency>

    and also under the dependency management

    01 <dependency>
    02 <groupId>org.keycloak.bom</groupId>
    03 <artifactId>keycloak-adapter-bom</artifactId>
    04 <version>10.0.0</version>
    05 <type>pom</type>
    06 <scope>import</scope>
    07 </dependency>
  2. Add the Keycloak adapter configuration class as follows:

    01 package org.example.service.config;
    02
    03 import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
    04 import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
    05 import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
    06 import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
    07 import org.springframework.beans.factory.annotation.Autowired;
    08 import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
    09 import org.springframework.context.annotation.Bean;
    10 import org.springframework.context.annotation.Import;
    11 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    12 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    13 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    14 import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
    15 import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
    16 import org.springframework.security.web.session.HttpSessionEventPublisher;
    17
    18 @KeycloakConfiguration
    19 @EnableGlobalMethodSecurity(prePostEnabled = true)
    20 @Import({KeycloakSpringBootConfigResolver.class})
    21 public class KeycloakAdapterConfig extends KeycloakWebSecurityConfigurerAdapter {
    22
    23 /* Registers the KeycloakAuthenticationProvider with the authentication manager.*/
    24
    25 @Autowired
    26 public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    27 KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
    28 keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
    29 auth.authenticationProvider(keycloakAuthenticationProvider);
    30 }
    31
    32
    33 /* Defines the session authentication strategy.*/
    34
    35 @Bean
    36 @Override
    37 protected NullAuthenticatedSessionStrategy sessionAuthenticationStrategy() {
    38 return new NullAuthenticatedSessionStrategy();
    39 }
    40 @Override
    41 protected void configure(HttpSecurity http) throws Exception
    42 {
    43 super.configure(http);
    44 http.authorizeRequests();
    45 }
    46 }
  3. Configure the connection to Keycloak service. Add the following values to our conf/application.yaml.

    01 keycloak:
    02 realm: sample
    03 auth-server-url: http://mfr-keycloak-keycloak.box.codenow-dev.codenow.com/auth
    04 resource: api
    05 public-client: true
    06 bearer-only: true

    Please note:

    realm: is our realm configured in Keycloak admin.

    auth-server-url: is our authentication endpoint (see above).

    resource: is our application identifier.

  4. Finally, secure our endpoints with @PreAuthorize annotation.

    01 @RestController
    02 @RequestMapping("/customers")
    03 public class CustomerController {
    04 @Autowired
    05 CustomerService customerService;
    06
    07 @PreAuthorize("hasRole('api-role')")
    08 @GetMapping
    09 public List<Customer> getAll() {
    10 return customerService.getAll();
    11 }
    12
    13 @PreAuthorize("hasRole('api-role')")
    14 @GetMapping("/{id}")
    15 public ResponseEntity<Customer> getCustomer(@PathVariable int id) {
    16 Customer customer = customerService.getById(id);
    17
    18 if (customer != null)
    19 return new ResponseEntity<>(customer, HttpStatus.ACCEPTED);
    20 else
    21 return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    22 }
    23 }

Testing in the local environment

  1. If you need to change the configuration parameters, for example the TCP port, please do so in config/application.yaml.

  2. Build and run our application. By default it runs on http://localhost:8080/customers

  3. Open the request in Postman, select "No Auth" as authorization type. You should receive a "401 Unauthorized" response

    potmanNoAuth
  4. Change the authorization type to "OAUTH2" and click on "Get New Access Token".

  5. Enter the Access Token URL, Username, Password and Client ID. Those have been set up in the "Keycloak installation and setup" chapter.

    postmanAccessToken
  6. You should get a new token, click on "Use Token"

  7. Send the request again and this time you should get the correct response.

    postmanSuccess

What’s next?

See our other developer tutorials: