Use keycloak with Spring boot
What is keycloak:
Keycloak is an open source Identity and Access Management solution aimed at modern applications and services. It makes it easy to secure applications and services with little to no code. This page gives a brief introduction to Keycloak and some of the features. For a full list of features refer to the documentation.
Keycloak developed by Redhat and is not the only IAM solution there other open source project such as Ory-Hydra built with GoLang, and I am sure there are other projects.
What is OAuth2:
OAuth 2.0 is the industry-standard protocol for authorization. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices
I will not get deep into what is Oauth, however you can check this out from the offical website: Oauth.
In short
OAuth is standard protocol used for authorization and Keycloak applying this protocol in addition to use authentication.
Keycloak could be used in two ways either use it as is and put it behind proxy server to access public APIs for account management, access tokens and refresh token and used directly with a frontend websites. You can check the official website about that here.
Or, to let your app is the front layer of APIs instead of using API gateway to do the required operations such as, authentication and generate token, refresh token and reset password or even create new user (this what I illustrate it here)
By applying this approach you will keep Keycloak behind and keep it simple with no Gateway ( this could fit small to medium business, otherwise use the API gateway ).
In order to use this design you have to have access for your API to call Keycloak with some admin permission we need to change some settings.
Before we start what is needed:
Before reaching this step you should be familiar with Oauth2, Java & Spring boot and at least what is Keycloak.
What software needed:
Use case:
Lets imagine that you would like to start a project that need user authentication and authorization, so you go and start creating a user DB model and repository and generate JWT token and create filter to authenticate and validate this token and much more to do.
In order to get rid of this hassle is to use OAuth server which provides the above and much more functionalities, lets begin with Keycloak and Spring boot.
Create project with maven and below is pom.xml
<?xml version="1.0" encoding="UTF-8">
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
?? ?xsi:schemaLocation="https://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.5.4</version>
?? ??? ?<relativePath/> <!-- lookup parent from repository -->
?? ?</parent>
?? ?<groupId>com.github.ahelmy</groupId>
?? ?<artifactId>keycloak-spring</artifactId>
?? ?<version>0.0.1-SNAPSHOT</version>
?? ?<name>Keycloak & Spring boot</name>
?? ?<description>Keycloak Spring boot integration</description>
?? ?<properties>
?? ??? ?<java.version>11</java.version>
?? ??? ?<org.mapstruct.version>1.4.2.Final</org.mapstruct.version>
?? ?</properties>
?? ?<dependencies>
?? ??? ?<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>
?? ??? ?</dependency>
?? ??? ?<dependency>
?? ??? ??? ?<groupId>org.keycloak</groupId>
?? ??? ??? ?<artifactId>keycloak-spring-boot-starter</artifactId>
?? ??? ?</dependency>
?? ??? ?<dependency>
?? ??? ??? ?<groupId>org.keycloak</groupId>
?? ??? ??? ?<artifactId>keycloak-admin-client</artifactId>
?? ??? ??? ?<version>14.0.0</version>
?? ??? ?</dependency>
?? ??? ?<dependency>
?? ??? ??? ?<groupId>org.mapstruct</groupId>
?? ??? ??? ?<artifactId>mapstruct</artifactId>
?? ??? ??? ?<version>${org.mapstruct.version}</version>
?? ??? ?</dependency>
?? ??? ?<!-- Test -->
?? ??? ?<dependency>
?? ??? ??? ?<groupId>org.springframework.boot</groupId>
?? ??? ??? ?<artifactId>spring-boot-devtools</artifactId>
?? ??? ??? ?<scope>runtime</scope>
?? ??? ??? ?<optional>true</optional>
?? ??? ?</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>
?? ?</dependencies>
?? ?<dependencyManagement>
?? ??? ?<dependencies>
?? ??? ??? ?<dependency>
?? ??? ??? ??? ?<groupId>org.keycloak.bom</groupId>
?? ??? ??? ??? ?<artifactId>keycloak-adapter-bom</artifactId>
?? ??? ??? ??? ?<version>13.0.1</version>
?? ??? ??? ??? ?<type>pom</type>
?? ??? ??? ??? ?<scope>import</scope>
?? ??? ??? ?</dependency>
?? ??? ?</dependencies>
?? ?</dependencyManagement>
?? ?<build>
?? ??? ?<plugins>
?? ??? ??? ?<plugin>
?? ??? ??? ??? ?<groupId>org.springframework.boot</groupId>
?? ??? ??? ??? ?<artifactId>spring-boot-maven-plugin</artifactId>
?? ??? ??? ??? ?<configuration>
?? ??? ??? ??? ??? ?<excludes>
?? ??? ??? ??? ??? ??? ?<exclude>
?? ??? ??? ??? ??? ??? ??? ?<groupId>org.projectlombok</groupId>
?? ??? ??? ??? ??? ??? ??? ?<artifactId>lombok</artifactId>
?? ??? ??? ??? ??? ??? ?</exclude>
?? ??? ??? ??? ??? ?</excludes>
?? ??? ??? ??? ?</configuration>
?? ??? ??? ?</plugin>
?? ??? ??? ?<plugin>
?? ??? ??? ??? ?<groupId>org.apache.maven.plugins</groupId>
?? ??? ??? ??? ?<artifactId>maven-compiler-plugin</artifactId>
?? ??? ??? ??? ?<version>3.8.1</version>
?? ??? ??? ??? ?<configuration>
?? ??? ??? ??? ??? ?<compilerArgs>--enable-preview</compilerArgs>
?? ??? ??? ??? ??? ?<source>${java.version}</source> <!-- depending on your project -->
?? ??? ??? ??? ??? ?<target>${java.version}</target> <!-- depending on your project -->
?? ??? ??? ??? ??? ?<annotationProcessorPaths>
?? ??? ??? ??? ??? ??? ?<path>
?? ??? ??? ??? ??? ??? ??? ?<groupId>org.mapstruct</groupId>
?? ??? ??? ??? ??? ??? ??? ?<artifactId>mapstruct-processor</artifactId>
?? ??? ??? ??? ??? ??? ??? ?<version>${org.mapstruct.version}</version>
?? ??? ??? ??? ??? ??? ?</path>
?? ??? ??? ??? ??? ??? ?<path>
?? ??? ??? ??? ??? ??? ??? ?<groupId>org.projectlombok</groupId>
?? ??? ??? ??? ??? ??? ??? ?<artifactId>lombok</artifactId>
?? ??? ??? ??? ??? ??? ??? ?<version>1.18.20</version>
?? ??? ??? ??? ??? ??? ?</path>
?? ??? ??? ??? ??? ??? ?<dependency>
?? ??? ??? ??? ??? ??? ??? ?<groupId>org.projectlombok</groupId>
?? ??? ??? ??? ??? ??? ??? ?<artifactId>lombok-mapstruct-binding</artifactId>
?? ??? ??? ??? ??? ??? ??? ?<version>0.2.0</version>
?? ??? ??? ??? ??? ??? ?</dependency>
?? ??? ??? ??? ??? ??? ?<!-- other annotation processors -->
?? ??? ??? ??? ??? ?</annotationProcessorPaths>
?? ??? ??? ??? ?</configuration>
?? ??? ??? ?</plugin>
?? ??? ?</plugins>
?? ?</build>
</project>
Now we configured a spring boot project with security dependency having also Keycloak admin client and Keycloak spring boot integration dependency that will do most of the magic.
<dependency>
?<groupId>org.keycloak</groupId>
?<artifactId>keycloak-spring-boot-starter</artifactId>
</dependency>
<dependency>
?<groupId>org.keycloak</groupId>
?<artifactId>keycloak-admin-client</artifactId>
?<version>14.0.0</version>
</dependency>
Create application.yml file contains Keycloak client configuration. you can replace {your-realm-name} with the realm we will create on Keycloak server and {realm-management-secret} with generated secret from Keycloak as described later.
keycloak
? auth-server-url: https://localhost:8080/auth
? realm: {your-realm-name}
? resource: realm-management
? credentials:
??? secret: {realm-management-secret}
Goto Keycloak and open bin folder then execute standalone.bat -b 0.0.0.0 "this is to allow binding on any IP"
After starting goto management console "https://localhost:8080/auth/" and set username and password.
What is realms?
领英推荐
A realm manages a set of users, credentials, roles, and groups. A user belongs to and logs into a realm. Realms are isolated from one another and can only manage and authenticate the users that they control.
From left click on "Add realm" button, then enter a name ex. "test-keycloak".
That's good step, please read this carefully.
Applying the below steps to allow access to Keycloak APIs and to have admin permission for users control.
Replace in application.yml the secret with the above secret you created and the realm name with your created realm name.
Lets prepare Spring boot to integrate, once we added the Spring security in dependency its auto configuration so in normal cases we create filter to check the validity of JWT and expiration to continue request. But here we also added Keycloak auto configuration and hence it do all that for us by declare Keycloak bean with below configuration.
Create new class for example name "KeycloakClient" contains:
import static org.keycloak.OAuth2Constants.CLIENT_CREDENTIALS;
import org.keycloak.admin.client.Keycloak;
import org.springframework.context.annotation.Bean;
import org.keycloak.admin.client.KeycloakBuilder;
import org.springframework.stereotype.Component;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@Component
public class KeycloakClient {
@Value("${keycloak.credentials.secret}"
??? private? String secretKey;
??? @Value("${keycloak.resource}")
??? private? String clientId;
??? @Value("${keycloak.auth-server-url}")
??? private? String authUrl;
??? @Value("${keycloak.realm}")
??? private? String realm;
??? @Bean
??? public Keycloak keycloakAdminClient(){
??????? return KeycloakBuilder.builder()
??????????????? .grantType(CLIENT_CREDENTIALS)
??????????????? .serverUrl(authUrl)
??????????????? .realm(realm)
??????????????? .clientId(clientId)
??????????????? .clientSecret(secretKey)
??????????????? .build();
??? }
}
And create new class for SecurityConfig:
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.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.Http403ForbiddenEntryPoint;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true,
??????? securedEnabled = true,
??????? jsr250Enabled = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
??? @Bean
??? public WebMvcConfigurer corsConfigurer() {
??????? return new WebMvcConfigurer() {
??????????? @Override
??????????? public void addCorsMappings(CorsRegistry registry) {
??????????????? registry.addMapping("/**").allowedOrigins("*");
??????????? }
??????? };
??? }
??? @Autowired
??? protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
??????? KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
??????? keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
??????? auth.authenticationProvider(keycloakAuthenticationProvider);
??? }
??? @Bean
??? public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
??????? return new KeycloakSpringBootConfigResolver();
??? }
??? @Bean
??? @Override
??? protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
??????? return new NullAuthenticatedSessionStrategy();
??? }
??? @Override
??? protected void configure(HttpSecurity http) throws Exception {
??????? super.configure(http);
??????? http.authorizeRequests()
??????????????? .antMatchers("/api/auth/token","/api/auth/refresh-token").permitAll()
??????????????? .anyRequest()
??????????????? .authenticated()
??????????????? .and().exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint());
??????? http.csrf().disable().cors();
??? }
};
Now, any request except "/api/auth/token" and "/api/auth/refresh-token" will be secured and require Authorization header with valid JWT.
We have to add two things one controller to handle token and refresh-token APIs and a service for that purpose.
import com.eshtrakaty.backend.dto.AuthenticationDto;
import com.eshtrakaty.backend.services.auth.AuthenticationService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auth")
public class AuthenticationController {
??? private final AuthenticationService authenticationService;
??? @PostMapping(value = "/token", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
??? public ResponseEntity<?> authenticateUser(@Valid @RequestBody AuthenticationDto authenticationDto) {
??????? return ResponseEntity.ok(this.authenticationService.authenticate(authenticationDto.getUsername(),authenticationDto.getPassword().toCharArray()));
??? }
??? @PostMapping(value = "/refresh-token", produces = MediaType.APPLICATION_JSON_VALUE)
??? public ResponseEntity<?> refreshToken(@RequestParam("refresh_token") String refreshToken) {
??????? return ResponseEntity.ok(this.authenticationService.refreshToken(refreshToken));
??? }
}
Service & implementation
import com.eshtrakaty.backend.dto.AccessToken
import com.eshtrakaty.backend.dto.OperationResult;
import com.eshtrakaty.backend.mappers.TokenMapper;
import com.eshtrakaty.backend.security.KeycloakClient;
import com.eshtrakaty.backend.services.auth.AuthenticationService;
import com.eshtrakaty.backend.utils.ExceptionUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.representations.AccessTokenResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
@Slf4j
@Service
@RequiredArgsConstructor
public class KeycloakAuthenticationServiceImpl implements AuthenticationService {
??? private final KeycloakClient keycloakClient;
??? private final RestTemplate restTemplate;
??? @Override
??? public AccessToken authenticate(String username, char[] password) {
??????? try {
??????????? Assert.notNull(username, "Username is null");
??????????? Assert.notNull(password, "Password is null");
??????????? var authzClient = keycloakClient.authzClient();
??????????? var authResponse = authzClient.obtainAccessToken(username, new String(password));
??????????? return TokenMapper.INSTANCE.accessTokenResponseToAccessToken(authResponse);
??????? }catch (Exception ex){
??????????? throw ExceptionUtil.getAuthenticationError(ex.getMessage());
??????? }
??? }
??? @Override
??? public AccessToken refreshToken(String refreshToken) {
??????? try {
??????????? Assert.notNull(refreshToken, "Refresh token is null");
??????????? var refreshTokenRequest = keycloakClient.getClient();
??????????? refreshTokenRequest.set("refresh_token", refreshToken);
??????????? refreshTokenRequest.set("grant_type", "refresh_token");
??????????? var headers = new HttpHeaders();
??????????? headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
??????????? headers.set(HttpHeaders.ACCEPT,MediaType.APPLICATION_JSON_VALUE);
??????????? var request = new HttpEntity<>(refreshTokenRequest,headers);
??????????? var authResponse =? restTemplate.postForEntity(keycloakClient.getTokenUrl(),request, AccessTokenResponse.class);
??????????? return TokenMapper.INSTANCE.accessTokenResponseToAccessToken(authResponse.getBody());
??????? }catch (Exception ex){
??????????? throw ExceptionUtil.getAuthenticationError(ex.getMessage());
??????? }
??? }
}
Service interface
import com.eshtrakaty.backend.dto.AccessToken
import com.eshtrakaty.backend.dto.OperationResult;
public interface AuthenticationService {
??? AccessToken authenticate(String username, char[] password);
??? AccessToken refreshToken(String refreshToken);
}
At this level we use KeycloakClient bean which will be created below to handle connection with Keycloak APIs this to obtain access token and refresh token in authentication step, however in refresh-token method there is no implementation in KeycloakClient for that so we need to call the REST APIs by ourselves with RestTemplate for example..
import com.fasterxml.jackson.databind.ObjectMapper
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
import static org.keycloak.OAuth2Constants.CLIENT_CREDENTIALS;
@Component
public class KeycloakClient {
??? @Value("${keycloak.credentials.secret}")
??? private? String secretKey;
??? @Value("${keycloak.resource}")
??? private? String clientId;
??? @Value("${keycloak.auth-server-url}")
??? private? String authUrl;
??? @Value("${keycloak.realm}")
??? private? String realm;
??? @Bean
??? public Keycloak keycloakAdminClient(){
??????? return KeycloakBuilder.builder()
??????????????? .grantType(CLIENT_CREDENTIALS)
??????????????? .serverUrl(authUrl)
??????????????? .realm(realm)
??????????????? .clientId(clientId)
??????????????? .clientSecret(secretKey)
??????????????? .build();
??? }
??? public AuthzClient authzClient(){
??????? var clientCredentials = new HashMap<String, Object>();
??????? clientCredentials.put("secret", secretKey);
??????? var configuration =
??????????????? new Configuration(authUrl, realm, clientId, clientCredentials, null);
??????? return AuthzClient.create(configuration);
??? }
??? @Bean
??? public RealmResource realmResource(Keycloak keycloak) {
??????? return keycloak.realm(realm);
??? }
??? public String getTokenUrl(){
??????? return authUrl+"/realms/"+realm+"/protocol/openid-connect/token";
??? }
??? public MultiValueMap<String, String> getClient(){
??????? var map =? new LinkedMultiValueMap<String, String>();
??????? map.set("client_id", clientId);
??????? map.set("client_secret", secretKey);
??????? return map;
??? }
}
As Keycloak starter handle authentication its by default store current logged in user in the SecurityContext and could be retrieved as example below for SecurityHelper class
import org.keycloak.KeycloakPrincipal
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
@Component
public class SecurityHelper {
??? public String getCurrentUserId(){
??????? var principal = (KeycloakPrincipal) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
??????? return principal.getKeycloakSecurityContext().getToken().getSubject();
??? }
}
And hence we can now have any controller with authorized requests with logged in user and also you can make it with Roles for created user with PreAuthroize annotation to create user, if you would like to create user from code also it could be done with below code:
import net.stemlabs.apps.medbook.dto.RegisterUserRequest;
import net.stemlabs.apps.medbook.dto.RegisterUserResult;
import net.stemlabs.apps.medbook.dto.UserDto;
import net.stemlabs.apps.medbook.entity.UserProfile;
import net.stemlabs.apps.medbook.events.UserEvent;
import net.stemlabs.apps.medbook.mapper.UserKeycloakMapper;
import net.stemlabs.apps.medbook.repository.UserProfileRepo;
import net.stemlabs.apps.medbook.service.user.UserService;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserServiceKeycloakImpl implements UserService {
??? @Autowired
??? private RealmResource keycloakRealmResource;
??? @Autowired
??? private UserProfileRepo userProfileRepo;
??? @Autowired
??? private ApplicationEventPublisher applicationEventPublisher;
??? @Override
??? public List<UserDto> findAll() {
??????? return keycloakRealmResource.users().list().stream().map(UserKeycloakMapper.INSTANCE::userRepresentationToUserDto).collect(Collectors.toList());
??? }
??? @Override
??? public RegisterUserResult create(RegisterUserRequest registerUserRequest) {
??????? var userRepresentation = UserKeycloakMapper.INSTANCE.registerUserRequestToUserRepresentation(registerUserRequest);
??????? var credential = new CredentialRepresentation();
??????? credential.setTemporary(false);
??????? credential.setValue(registerUserRequest.getPassword());
??????? credential.setType(CredentialRepresentation.PASSWORD);
??????? userRepresentation.setEnabled(true);
??????? userRepresentation.setCredentials(Collections.singletonList(credential));
??????? var response = keycloakRealmResource.users().create(userRepresentation);
??????? switch(response.getStatus()){
??????????? case 201:
??????????????? var location = (String) response.getHeaders().getFirst("Location");
??????????????? var userId = location.substring(location.lastIndexOf("/")+1);
??????????????? applicationEventPublisher.publishEvent(new UserEvent(userId, UserEvent.UserEventType.USER_REGISTERED));
??????????????? return new RegisterUserResult(userId);
??????????? case 409:
??????????????? throw new RuntimeException("Username already exists");
??????????? default:
??????????????? throw new RuntimeException("Failed to create account");
??????? }
??? }
}
Conclusion:
There is always different ways to do the same thing, but every way has its pros and cons and suitable case for use.
Your comments or questions are highly appreciated.
KeycloakClient class
Software Engineer
2 年Thanks for this. Will be helpful as others mentioned to include the source code.
JavaEE | Spring Boot | Microservices | SQL/PLSQL | ACMP (IBA, DU)
2 年This read is really helpful. Can I have the full source code link... Thanks a lot.
Software Engineer - Java
2 年Thanks for sharing
Team Lead Manager at EGYAAAC(project based)
3 年Great
Tech Manager | Tech Lead | Java | Cisco (PCCE - CVP - IVR) | Sprinklr | WFM | VOC | QM | Solution Arch at First Digital Bank In Egypt
3 年Professional and rich artical , AT the end any one no matter his backgroud , can implement the KeyCloak to Auth APIs Keep Going and waiting for next artical , ??