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.
  • You are already familiar with Postman (REST client) basics.

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 Keycloak endpoints. We will need them later in our application.

  • Authorization endpoint is composed of the base URL of your Keycloak instance + "/auth". For example "...mfr-keycloak-keycloak.box.codenow-dev.codenow.com/auth".

  • Token endpoint can be found under Realm Settings / General / Endpoints. When you click on the link "OpenID Endpoint Configuration", you should get a response similar to the one on the screen bellow.

    keycloakAddRealm

We are done with the basic Keycloak setup.

Securing our SpringBoot application#

  1. Add the following dependencies to pom.xml

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    </dependency>
    <dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-security-adapter</artifactId>
    <version>10.0.0</version>
    </dependency>

    and also under the dependency management

    <dependency>
    <groupId>org.keycloak.bom</groupId>
    <artifactId>keycloak-adapter-bom</artifactId>
    <version>10.0.0</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
  2. Add the Keycloak adapter configuration class as follows:

    package org.example.service.config;
    import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
    import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
    import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
    import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Import;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
    import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
    import org.springframework.security.web.session.HttpSessionEventPublisher;
    @KeycloakConfiguration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @Import({KeycloakSpringBootConfigResolver.class})
    public class KeycloakAdapterConfig extends KeycloakWebSecurityConfigurerAdapter {
    /* Registers the KeycloakAuthenticationProvider with the authentication manager.*/
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
    keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
    auth.authenticationProvider(keycloakAuthenticationProvider);
    }
    /* Defines the session authentication strategy.*/
    @Bean
    @Override
    protected NullAuthenticatedSessionStrategy sessionAuthenticationStrategy() {
    return new NullAuthenticatedSessionStrategy();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
    super.configure(http);
    http.authorizeRequests();
    }
    }
  3. Configure the connection to Keycloak service. Add the following values to our conf/application.yaml.

    keycloak:
    realm: sample
    auth-server-url: http://mfr-keycloak-keycloak.box.codenow-dev.codenow.com/auth
    resource: api
    public-client: true
    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.

    @RestController
    @RequestMapping("/customers")
    public class CustomerController {
    @Autowired
    CustomerService customerService;
    @PreAuthorize("hasRole('api-role')")
    @GetMapping
    public List<Customer> getAll() {
    return customerService.getAll();
    }
    @PreAuthorize("hasRole('api-role')")
    @GetMapping("/{id}")
    public ResponseEntity<Customer> getCustomer(@PathVariable int id) {
    Customer customer = customerService.getById(id);
    if (customer != null)
    return new ResponseEntity<>(customer, HttpStatus.ACCEPTED);
    else
    return new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
    }

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: