Single Sign-On with Keycloak 17.0.1 and Spring Boot 2.6 or later.
Single Sign-On (SSO)
Single Sign-On Is an authentication method that permits a user to use one set of login credentials with multiple application.
Keycloak
Keycloak is an open source identity and access management for modern applications and services, no need to deal with storing users or authenticating users. it's all available out of the box.
You'll even get advanced features such as user federation (LDAP or Kerberos) and social login.
The Documentation keycloak mentioned the following with respect SSO: Users authenticate with Keycloak rather than individual applications. This means that your applications don't have to deal with login forms, authenticating users, and storing users. Once logged-in to Keycloak, users don't have to login again to access a different application.
This also applied to logout. Keycloak provides single-sign out, which means users only have to logout once to be logged-out of all applications that use Keycloak.
Downloading and installing keycloack by Docker
For Install keycloak by docker you just need the next command:
docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:17.0.1 start-dev
This will start Keycloak exposed on the local port 8080. It will also create an initial admin user with username admin and password admin.
Setting Up a keycloack server.
Open a browser http://localhost:8080 or http://127.0.0.1:8080 result this.
We can now proceed to the Administrative Console.
Creating a Realm
For default the console opens up Master realm, Let's navigate left corner Add realm button.
For this example I created realm demo. we'll click the button create.
Creating a Client
Add a new client with the name spring-boot-keycloak with openid-connect. click button save.
Add Valid Redirect URIs.
Creating a Role
Add next role, user.
Creating a Users
Add next user: user1 password user1 and role user.
Set a role.
Creating a Spring Boot Application
In this examples we're integration Spring Boot and keycloack.
- Configuration Spring boot, Spring Data and keycloack.
- Define Data Models, Repository.
- Spring Controllers
Technology
- Java 17
- Spring Boot 2.6.6
- HyperSQL 2.5.2
- Maven 3.6.3
- Thymeleaf 2.6.6
- IntelliJ IDEA
Configuration Spring Boot Keycloak project
- Spring Initializr or Spring Suite Tool (STS).
- Maven
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.henry</groupId>
<artifactId>sso-spring-boot-keycloak</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sso-spring-boot-keycloak</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
<keycloak-adapter-bom.version>17.0.1</keycloak-adapter-bom.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak-adapter-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8081
keycloak:
auth-server-url: http://127.0.0.1:8080
realm: demo
resource: spring-boot-keycloak
public-client: true
principal-attribute: preferred_usernameClass 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.ssospringbootkeycloak.configuration;
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.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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
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()
.antMatchers("/customers*", "/home*", "/info*")
.hasRole("user")
.anyRequest()
.permitAll();
}
}package com.henry.ssospringbootkeycloak.configuration;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class KeycloakConfig {
@Bean
public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}Data class model
package com.henry.ssospringbootkeycloak.model;
import javax.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@Table
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String serviceRendered;
private String address;
}Repository Interface
package com.henry.ssospringbootkeycloak.repository;
import com.henry.ssospringbootkeycloak.model.Customer;
import org.springframework.data.repository.CrudRepository;
public interface CustomerDAO extends CrudRepository<Customer, Long> {
}Spring Controller
package com.henry.ssospringbootkeycloak.controller;
import com.henry.ssospringbootkeycloak.model.Customer;
import com.henry.ssospringbootkeycloak.repository.CustomerDAO;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Date;
@Controller
public class SsoController {
private final CustomerDAO customerDAO;
public SsoController(CustomerDAO customerDAO) {
this.customerDAO = customerDAO;
}
@GetMapping(path = "/")
public String index() {
return "redirect:/home";
}
@GetMapping(path = "/home")
public String home(Model model) {
model.addAttribute("date", new Date());
return "home";
}
@GetMapping(path = "/info")
public String info(Principal principal, Model model) {
model.addAttribute("username", principal.getName());
model.addAttribute("birthday", -1);
return "user";
}
@GetMapping("/logout")
public String logout(HttpServletRequest request) throws Exception {
request.logout();
return "redirect:/";
}
@GetMapping(path = "/customers")
public String customers(Principal principal, Model model) {
addCustomers();
Iterable<Customer> customers = customerDAO.findAll();
model.addAttribute("customers", customers);
model.addAttribute("username", principal.getName());
return "customers";
}
// add customers for demonstration
public void addCustomers() {
Customer customer1 = new Customer();
customer1.setAddress("123");
customer1.setName("pharmaceutical Industries");
customer1.setServiceRendered("Important services");
customerDAO.save(customer1);
Customer customer2 = new Customer();
customer2.setAddress("1234");
customer2.setName("technologies Industries");
customer2.setServiceRendered("Important services");
customerDAO.save(customer2);
}
}Run & Test
Run Spring Boot application with command: mvn spring-boot:run. by console or IntelliJ etc.Open a browser http://localhost:8081 or http://127.0.0.1:8081 result this.
Set username user1 and password user1 , keycloak suggest to you to change the password,In this example I set the same password.And resul this.Logout back the login.References.
https://www.baeldung.com/spring-boot-keycloakhttps://www.keycloak.org/getting-started/getting-started-dockerhttps://www.keycloak.org/https://www.onelogin.com/learn/how-single-sign-on-workshttps://www.techtarget.com/searchsecurity/definition/single-sign-on