Secure Your Java Applications: A Complete Guide to Keycloak OIDC Integration

Keycloak is an open-source Identity and Access Management solution that provides robust OIDC (OpenID Connect) and OAuth 2.0 capabilities. This guide covers everything from basic setup to advanced integration patterns for Java applications.

Architecture Overview

Java Application → Keycloak OIDC → Users
↑
(Secure APIs with
JWT Tokens)

Step 1: Keycloak Server Setup

Docker Compose for Keycloak

# docker-compose.yml
version: '3.8'
services:
keycloak:
image: quay.io/keycloak/keycloak:24.0.1
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
KC_HOSTNAME: localhost
KC_HTTP_ENABLED: true
KC_HOSTNAME_STRICT: false
ports:
- "8080:8080"
command:
- start-dev
networks:
- keycloak-network
postgres:
image: postgres:15
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- keycloak-network
volumes:
postgres_data:
networks:
keycloak-network:

Realm and Client Configuration

// src/main/java/com/company/keycloak/KeycloakInitializer.java
package com.company.keycloak;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import javax.ws.rs.core.Response;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Programmatic Keycloak realm and client setup
*/
public class KeycloakInitializer {
private final Keycloak keycloak;
private final String serverUrl;
public KeycloakInitializer(String serverUrl, String adminUsername, String adminPassword) {
this.serverUrl = serverUrl;
this.keycloak = KeycloakBuilder.builder()
.serverUrl(serverUrl)
.realm("master")
.username(adminUsername)
.password(adminPassword)
.clientId("admin-cli")
.build();
}
public void createRealm(String realmName) {
RealmRepresentation realm = new RealmRepresentation();
realm.setRealm(realmName);
realm.setEnabled(true);
realm.setRegistrationAllowed(true);
realm.setRememberMe(true);
// Create realm
keycloak.realms().create(realm);
// Create client
createClient(realmName, "java-app", "http://localhost:8081/*");
createClient(realmName, "java-app-backend", "http://localhost:8081/*");
// Create users
createUser(realmName, "[email protected]", "password", "USER");
createUser(realmName, "[email protected]", "password", "ADMIN", "USER");
}
private void createClient(String realmName, String clientId, String redirectUri) {
ClientRepresentation client = new ClientRepresentation();
client.setClientId(clientId);
client.setPublicClient(true);
client.setRedirectUris(Collections.singletonList(redirectUri));
client.setWebOrigins(Collections.singletonList("*"));
client.setDirectAccessGrantsEnabled(true);
client.setEnabled(true);
keycloak.realm(realmName).clients().create(client);
}
private void createUser(String realmName, String username, String password, String... roles) {
UserRepresentation user = new UserRepresentation();
user.setUsername(username);
user.setEmail(username);
user.setEnabled(true);
user.setEmailVerified(true);
// Create user
Response response = keycloak.realm(realmName).users().create(user);
if (response.getStatus() == 201) {
String userId = response.getLocation().getPath().replaceAll(".*/([^/]+)$", "$1");
// Set password
CredentialRepresentation credential = new CredentialRepresentation();
credential.setType(CredentialRepresentation.PASSWORD);
credential.setValue(password);
credential.setTemporary(false);
keycloak.realm(realmName).users().get(userId).resetPassword(credential);
// Assign roles
assignRoles(realmName, userId, roles);
}
}
private void assignRoles(String realmName, String userId, String[] roles) {
// Implementation for role assignment
}
}

Step 2: Spring Boot OIDC Integration

Dependencies

<!-- pom.xml -->
<dependencies>
<!-- Spring Boot Starter OAuth2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<!-- Keycloak Adapter -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>24.0.1</version>
</dependency>
<!-- JWT Support -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>

Application Configuration

# application.yml
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: java-app
client-secret: ${KEYCLOAK_CLIENT_SECRET}
scope: openid,profile,email
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/keycloak"
provider:
keycloak:
issuer-uri: http://localhost:8080/realms/company-realm
user-name-attribute: preferred_username
resourceserver:
jwt:
issuer-uri: http://localhost:8080/realms/company-realm
keycloak:
auth-server-url: http://localhost:8080
realm: company-realm
resource: java-app-backend
public-client: true
bearer-only: true
server:
port: 8081

Security Configuration

// src/main/java/com/company/config/SecurityConfig.java
package com.company.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {
private final JwtAuthFilter jwtAuthFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.disable())
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> 
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// Public endpoints
.authorizeHttpRequests(authz -> authz
.requestMatchers("/", "/public/**", "/auth/login", "/auth/callback").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
// OAuth2 Login configuration
.oauth2Login(oauth2 -> oauth2
.loginPage("/auth/login")
.defaultSuccessUrl("/dashboard")
.failureUrl("/auth/login?error=true")
)
// OAuth2 Resource Server for JWT
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
)
// Add JWT filter
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
}

Step 3: JWT Authentication and Authorization

JWT Authentication Filter

// src/main/java/com/company/security/JwtAuthFilter.java
package com.company.security;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
@Component
@Slf4j
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final ObjectMapper objectMapper;
@Value("${keycloak.auth-server-url}")
private String keycloakUrl;
@Value("${keycloak.realm}")
private String realm;
@Override
protected void doFilterInternal(HttpServletRequest request, 
HttpServletResponse response, 
FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
try {
Claims claims = validateAndParseToken(token);
setAuthenticationContext(claims, token);
} catch (Exception e) {
log.error("JWT validation failed", e);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("Invalid token");
return;
}
}
filterChain.doFilter(request, response);
}
private Claims validateAndParseToken(String token) {
// In production, use proper JWT validation with Keycloak's public key
// This is a simplified example
return Jwts.parserBuilder()
.setSigningKey(getPublicKey())
.build()
.parseClaimsJws(token)
.getBody();
}
private PublicKey getPublicKey() {
try {
// In real implementation, fetch from Keycloak's JWKS endpoint
String publicKey = "YOUR_KEYCLOAK_PUBLIC_KEY";
byte[] keyBytes = Base64.getDecoder().decode(publicKey);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
} catch (Exception e) {
throw new RuntimeException("Failed to get public key", e);
}
}
private void setAuthenticationContext(Claims claims, String token) {
String username = claims.getSubject();
@SuppressWarnings("unchecked")
List<String> roles = claims.get("roles", List.class);
List<SimpleGrantedAuthority> authorities = roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
UsernamePasswordAuthenticationToken authentication = 
new UsernamePasswordAuthenticationToken(username, token, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}

User Information Service

// src/main/java/com/company/service/UserService.java
package com.company.service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
@Slf4j
@RequiredArgsConstructor
public class UserService {
public UserInfo getCurrentUserInfo() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof OidcUser oidcUser) {
return extractUserInfoFromOidc(oidcUser);
} else if (principal instanceof Jwt jwt) {
return extractUserInfoFromJwt(jwt);
} else if (principal instanceof String) {
return UserInfo.builder()
.username((String) principal)
.build();
}
throw new IllegalStateException("Unknown principal type");
}
private UserInfo extractUserInfoFromOidc(OidcUser oidcUser) {
Map<String, Object> claims = oidcUser.getClaims();
return UserInfo.builder()
.username(oidcUser.getPreferredUsername())
.email(oidcUser.getEmail())
.firstName(oidcUser.getGivenName())
.lastName(oidcUser.getFamilyName())
.roles(oidcUser.getAuthorities().stream()
.map(auth -> auth.getAuthority().replace("ROLE_", ""))
.toList())
.token(oidcUser.getIdToken().getTokenValue())
.claims(claims)
.build();
}
private UserInfo extractUserInfoFromJwt(Jwt jwt) {
Map<String, Object> claims = jwt.getClaims();
return UserInfo.builder()
.username(jwt.getClaimAsString("preferred_username"))
.email(jwt.getClaimAsString("email"))
.firstName(jwt.getClaimAsString("given_name"))
.lastName(jwt.getClaimAsString("family_name"))
.roles(jwt.getClaimAsStringList("roles"))
.token(jwt.getTokenValue())
.claims(claims)
.build();
}
@Data
@Builder
public static class UserInfo {
private String username;
private String email;
private String firstName;
private String lastName;
private List<String> roles;
private String token;
private Map<String, Object> claims;
public boolean hasRole(String role) {
return roles != null && roles.contains(role);
}
public boolean isAdmin() {
return hasRole("ADMIN");
}
}
}

Step 4: REST API Controllers

Authentication Controller

// src/main/java/com/company/controller/AuthController.java
package com.company.controller;
import com.company.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
@Controller
@RequestMapping("/auth")
@RequiredArgsConstructor
@Slf4j
public class AuthController {
private final UserService userService;
@GetMapping("/login")
public String loginPage(Model model) {
return "login";
}
@GetMapping("/userinfo")
@ResponseBody
public UserService.UserInfo getUserInfo() {
return userService.getCurrentUserInfo();
}
@PostMapping("/logout")
public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {
new SecurityContextLogoutHandler().logout(request, response, 
SecurityContextHolder.getContext().getAuthentication());
response.sendRedirect("/");
}
@GetMapping("/callback")
public String oauthCallback() {
return "redirect:/dashboard";
}
}

Protected API Controllers

// src/main/java/com/company/controller/UserController.java
package com.company.controller;
import com.company.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/profile")
public ResponseEntity<UserService.UserInfo> getProfile() {
return ResponseEntity.ok(userService.getCurrentUserInfo());
}
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Map<String, String>> adminEndpoint() {
return ResponseEntity.ok(Map.of("message", "Hello Admin!"));
}
@GetMapping("/user")
@PreAuthorize("hasAnyRole('USER', 'ADMIN')")
public ResponseEntity<Map<String, String>> userEndpoint() {
return ResponseEntity.ok(Map.of("message", "Hello User!"));
}
}
// Product API with method-level security
@RestController
@RequestMapping("/api/products")
@PreAuthorize("hasRole('USER')")
public class ProductController {
@GetMapping
public ResponseEntity<List<Product>> getProducts() {
// Implementation
return ResponseEntity.ok(Collections.emptyList());
}
@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Product> createProduct(@RequestBody Product product) {
// Implementation
return ResponseEntity.ok(product);
}
@Data
public static class Product {
private Long id;
private String name;
private String description;
}
}

Step 5: Keycloak Admin Client

Admin Client Configuration

// src/main/java/com/company/keycloak/KeycloakAdminService.java
package com.company.keycloak;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.representations.idm.UserRepresentation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@Slf4j
@RequiredArgsConstructor
public class KeycloakAdminService {
@Value("${keycloak.auth-server-url}")
private String serverUrl;
@Value("${keycloak.realm}")
private String realm;
@Value("${keycloak.admin.username}")
private String adminUsername;
@Value("${keycloak.admin.password}")
private String adminPassword;
private Keycloak getKeycloakInstance() {
return KeycloakBuilder.builder()
.serverUrl(serverUrl)
.realm("master")
.username(adminUsername)
.password(adminPassword)
.clientId("admin-cli")
.build();
}
public List<UserRepresentation> getUsers() {
try (Keycloak keycloak = getKeycloakInstance()) {
return keycloak.realm(realm).users().list();
}
}
public UserRepresentation getUserById(String userId) {
try (Keycloak keycloak = getKeycloakInstance()) {
return keycloak.realm(realm).users().get(userId).toRepresentation();
}
}
public void createUser(String username, String email, String password) {
try (Keycloak keycloak = getKeycloakInstance()) {
UserRepresentation user = new UserRepresentation();
user.setUsername(username);
user.setEmail(email);
user.setEnabled(true);
user.setEmailVerified(true);
keycloak.realm(realm).users().create(user);
log.info("Created user: {}", username);
}
}
public void assignRoleToUser(String userId, String roleName) {
try (Keycloak keycloak = getKeycloakInstance()) {
// Implementation for role assignment
}
}
}

Step 6: Frontend Integration

Thymeleaf Templates

<!-- src/main/resources/templates/login.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h4 class="text-center">Login with Keycloak</h4>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<a th:href="@{/oauth2/authorization/keycloak}" 
class="btn btn-primary btn-lg">
Sign in with Keycloak
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
<!-- src/main/resources/templates/dashboard.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<span class="navbar-brand">Java App</span>
<div class="navbar-nav ms-auto">
<span class="navbar-text me-3" th:text="${user.username}"></span>
<form th:action="@{/auth/logout}" method="post" class="d-inline">
<button type="submit" class="btn btn-outline-light btn-sm">Logout</button>
</form>
</div>
</div>
</nav>
<div class="container mt-4">
<h2>Welcome, <span th:text="${user.firstName}"></span>!</h2>
<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-header">User Information</div>
<div class="card-body">
<p><strong>Username:</strong> <span th:text="${user.username}"></span></p>
<p><strong>Email:</strong> <span th:text="${user.email}"></span></p>
<p><strong>Roles:</strong> <span th:text="${user.roles}"></span></p>
</div>
</div>
</div>
</div>
<div class="row mt-4" th:if="${user.admin}">
<div class="col-12">
<div class="card border-warning">
<div class="card-header bg-warning">Admin Panel</div>
<div class="card-body">
<a href="/admin" class="btn btn-warning">Go to Admin Panel</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

Step 7: Testing

Test Configuration

// src/test/java/com/company/config/TestSecurityConfig.java
@Configuration
@TestPropertySource(properties = {
"spring.security.oauth2.client.registration.keycloak.client-id=test-client",
"spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8080/realms/test"
})
public class TestSecurityConfig {
@Bean
@Primary
public UserService mockUserService() {
UserService userService = mock(UserService.class);
when(userService.getCurrentUserInfo()).thenReturn(
UserService.UserInfo.builder()
.username("testuser")
.email("[email protected]")
.roles(List.of("USER"))
.build()
);
return userService;
}
}
// src/test/java/com/company/controller/UserControllerTest.java
@WebMvcTest(UserController.class)
@Import(TestSecurityConfig.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
@WithMockUser(roles = "USER")
void testGetProfile() throws Exception {
UserService.UserInfo userInfo = UserService.UserInfo.builder()
.username("testuser")
.email("[email protected]")
.roles(List.of("USER"))
.build();
when(userService.getCurrentUserInfo()).thenReturn(userInfo);
mockMvc.perform(get("/api/users/profile")
.header("Authorization", "Bearer mock-token"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.username").value("testuser"))
.andExpect(jsonPath("$.email").value("[email protected]"));
}
@Test
@WithMockUser(roles = "ADMIN")
void testAdminEndpoint() throws Exception {
mockMvc.perform(get("/api/users/admin"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("Hello Admin!"));
}
@Test
@WithMockUser(roles = "USER")
void testAdminEndpointWithoutPermission() throws Exception {
mockMvc.perform(get("/api/users/admin"))
.andExpect(status().isForbidden());
}
}

Step 8: Advanced Features

Custom Token Converter

// src/main/java/com/company/security/CustomJwtAuthenticationConverter.java
@Component
public class CustomJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
@Override
public AbstractAuthenticationToken convert(Jwt jwt) {
Collection<GrantedAuthority> authorities = extractAuthorities(jwt);
String username = jwt.getClaimAsString("preferred_username");
return new JwtAuthenticationToken(jwt, authorities, username);
}
private Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
Map<String, Object> realmAccess = jwt.getClaimAsMap("realm_access");
List<String> roles = (List<String>) realmAccess.get("roles");
if (roles == null) {
return Collections.emptyList();
}
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
}
}

Rate Limiting by User

// src/main/java/com/company/security/RateLimitFilter.java
@Component
@Slf4j
public class RateLimitFilter extends OncePerRequestFilter {
private final RateLimiter rateLimiter = RateLimiter.create(100); // 100 requests per second
@Override
protected void doFilterInternal(HttpServletRequest request, 
HttpServletResponse response, 
FilterChain filterChain) throws ServletException, IOException {
if (!rateLimiter.tryAcquire()) {
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
response.getWriter().write("Rate limit exceeded");
return;
}
filterChain.doFilter(request, response);
}
}

Configuration for Different Environments

application-prod.yml

spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: ${KEYCLOAK_CLIENT_ID}
client-secret: ${KEYCLOAK_CLIENT_SECRET}
provider:
keycloak:
issuer-uri: ${KEYCLOAK_ISSUER_URI}
keycloak:
auth-server-url: ${KEYCLOAK_SERVER_URL}
realm: ${KEYCLOAK_REALM}
resource: ${KEYCLOAK_CLIENT_ID}
logging:
level:
org.springframework.security: INFO
com.company: DEBUG

Best Practices

  1. Security
  • Use HTTPS in production
  • Validate JWT signatures properly
  • Implement proper CORS configuration
  • Use secure cookie settings
  1. Performance
  • Cache public keys from Keycloak
  • Use connection pooling for Admin API
  • Implement rate limiting
  1. Monitoring
  • Log authentication events
  • Monitor token expiration
  • Track failed login attempts
  1. Error Handling
  • Graceful handling of Keycloak downtime
  • Proper error messages for users
  • Logout on token validation failures

Conclusion

Keycloak OIDC integration provides:

  • Centralized Authentication: Single sign-on across applications
  • Standards Compliance: OIDC and OAuth 2.0 compliance
  • Flexible Authorization: Role-based access control
  • Security: Industry-standard security practices

Implementation steps:

  1. Set up Keycloak server and configure realm
  2. Integrate Spring Security OAuth2
  3. Implement JWT validation and user extraction
  4. Create protected APIs with role-based security
  5. Add Admin client for user management
  6. Implement frontend integration
  7. Add testing and monitoring

This setup provides a robust, secure authentication and authorization system that can scale from small applications to enterprise systems.

Java Logistics, Shipping Integration & Enterprise Inventory Automation (Tracking, ERP, RFID & Billing Systems)

https://macronepal.com/blog/aftership-tracking-in-java-enterprise-package-visibility/
Explains how to integrate AfterShip tracking services into Java applications to provide real-time shipment visibility, delivery status updates, and centralized tracking across multiple courier services.

https://macronepal.com/blog/shipping-integration-using-fedex-api-with-java-for-logistics-automation/
Explains how to integrate the FedEx API into Java systems to automate shipping tasks such as creating shipments, calculating delivery costs, generating shipping labels, and tracking packages.

https://macronepal.com/blog/shipping-and-logistics-integrating-ups-apis-with-java-applications/
Explains UPS API integration in Java to enable automated shipping operations including rate calculation, shipment scheduling, tracking, and delivery confirmation management.

https://macronepal.com/blog/generating-and-reading-qr-codes-for-products-in-java/
Explains how Java applications generate and read QR codes for product identification, tracking, and authentication, supporting faster inventory handling and product verification processes.

https://macronepal.com/blog/designing-a-robust-pick-and-pack-workflow-in-java/
Explains how to design an efficient pick-and-pack workflow in Java warehouse systems, covering order processing, item selection, packaging steps, and logistics preparation to improve fulfillment efficiency.

https://macronepal.com/blog/rfid-inventory-management-system-in-java-a-complete-guide/
Explains how RFID technology integrates with Java applications to automate inventory tracking, reduce manual errors, and enable real-time stock monitoring in warehouses and retail environments.

https://macronepal.com/blog/erp-integration-with-odoo-in-java/
Explains how Java applications connect with Odoo ERP systems to synchronize inventory, orders, customer records, and financial data across enterprise systems.

https://macronepal.com/blog/automated-invoice-generation-creating-professional-excel-invoices-with-apache-poi-in-java/
Explains how to automatically generate professional Excel invoices in Java using Apache POI, enabling structured billing documents and automated financial record creation.

https://macronepal.com/blog/enterprise-financial-integration-using-quickbooks-api-in-java-applications/
Explains QuickBooks API integration in Java to automate financial workflows such as invoice management, payment tracking, accounting synchronization, and financial reporting.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper