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:
Methods | Urls |
---|---|
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
- I recomend to you use Spring Initializr or Spring Suite Tool (STS).
- Maven
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.
References.
https://www.baeldung.com/spring-boot-keycloak
https://www.youtube.com/watch?v=rcvAmBoDlLk