Tuesday, December 7, 2021

Secure Spring boot 2 with Keycloak 15

Secure Spring boot 2 with Keycloak 15

We'll cover the basics of setting up a keycloak server, connecting spring boot microservices, also using spring security.

1. Setting Up a keycloack server.

Please check the steps to install keycloak server here.

2. Creating a Realm

For default the console opens up Master realm, Let's navigate left corner Add realm button.













For this example I typed the name demo1. we'll click the button create.













3. Creating a client









Add a new client with the name springboot-keycloak with openid-connect. click button save.
































We'll be creating a spring boot microservices running at the port 8081, we've used a redirect URL of http://localhost:8081 above on image.

4. Creating a Role








Add next roles, "admin" and "user".
















5. Creating a Users

Add next users: user1 to role user and user2 to role admin.










Set a password. I typed the password for the same user1 and user2. For this example.






















Set a role(s).

















You'll do the same steps with de user2 however the role is "admin".







5. Creating a Spring Boot Application

In this examples we're intregation Spring Boot and keycloack.

  • Configuration Spring boot, Spring Data and Keycloack.
  • Define Datas Models, Repository.
  • Spring Rest Controllers  


Spring Boot Rest  API 

These are APIs that we need to provide:

MethodsUrls
POST/users
GET/users

Technology

  • Java 11
  • Spring Boot 2.6.1
  • Mysql 8.0.23
  • Maven 3.6.3
  • Postman v9.0.8
  • IntelliJ IDEA 2021.2.3 

Project Structure


























Configuration Spring Boot Keycloak project 


pom.xml


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Exclude the Tomcat dependency -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use Jetty instead -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>

application.yml


server:
port: 8081

keycloak:
auth-server-url: http://127.0.0.1:9080/auth
realm: demo1
resource: springboot-keycloack
public-client: true
bearer-only: true

spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/backend?allowPublicKeyRetrieval=true&useSSL=false
username: henry
password: *********
hikari:
initialization-fail-timeout: 0
jpa:
database-platform: org.hibernate.dialect.MySQL5Dialect
generate-ddl: true
show-sql: true
hibernate:
ddl-auto: create

Class Security configuration


I suggest to you, we 'll be reading Spring Boot Adapters, I copied from Spring Boot Adapters the configuration class.

package com.henry.keycloack.springboot2keycloak.configuration;

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.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.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

@KeycloakConfiguration
@Import(KeycloakSpringBootConfigResolver.class)
@EnableGlobalMethodSecurity(jsr250Enabled = true)//so i need the roles exist environment, @RolesAllowed
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
{
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

KeycloakAuthenticationProvider keycloakAuthenticationProvider = new KeycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}

/**
* Defines the session authentication strategy.
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}

@Override
protected void configure(HttpSecurity http) throws Exception
{
super.configure(http);
http
.authorizeRequests()
.anyRequest().permitAll();
http.csrf().disable();
}
}


Data class model


package com.henry.keycloack.springboot2keycloak.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table
public class User {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
}

Repository Interface

package com.henry.keycloack.springboot2keycloak.repository;

import com.henry.keycloack.springboot2keycloak.model.User;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User,Integer> {
}


Spring Rest APIs Controller

package com.henry.keycloack.springboot2keycloak.controller;

import com.henry.keycloack.springboot2keycloak.model.User;
import com.henry.keycloack.springboot2keycloak.repository.UserRepository;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.security.RolesAllowed;

@RestController
public class UserController {

private final UserRepository userRepository;

public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}

@PostMapping("/users")
@RolesAllowed({"user"})
public ResponseEntity<User> save(@RequestBody User user){
return ResponseEntity.ok(userRepository.save(user));
}

@GetMapping("/users")
@RolesAllowed("admin")
public ResponseEntity<Iterable<User>> findAll(){
return ResponseEntity.ok(userRepository.findAll());
}
}

Run & Test


Run Spring Boot application with command: mvn spring-boot:run. by console, IntelliJ etc.

Keycloak provides a REST API for generating and refreshing access tokens. 

Let's go.


1. We need to acquire an access token from Keycloak from this url. 

  • 127.0.0.1 if you want will change to localhost.
  • Port: 9080 in my case.
  • auth/realms url defined by keycloak.
  • demo1 is the name of your realm.
  • protocol/openid-connect/token url defined by keycloak.

 http://127.0.0.1:9080/auth/realms/demo1/protocol/openid-connect/token


2. Configurate on Postman.


GET. 

I used the user "user2" role "admin", we'll check images. 















Access Token url. http://127.0.0.1:9080/auth/realms/demo1/protocol/openid-connect/token















Click on Get New Acces token.


















Click Use Token.






































POST. 

I used the user "user1" role "user", we'll check images. 




































Source Code


Here on Github.




References.

https://www.baeldung.com/spring-boot-keycloak
https://www.youtube.com/watch?v=rcvAmBoDlLk






No comments:

Post a Comment

Virtual Threads in Java 21: Simplified Concurrency for Modern Applications

  With Java 21, Virtual Threads have redefined how we approach concurrency, offering a lightweight and efficient way to handle parallel and ...