Spring Boot lets you combine LDAP authentication with JWE-encrypted JWTs to secure your Spring Boot APIs with both external directory integration and token confidentiality.
Written by
Şuayb Şimşek
Backend-focused fullstack developer sharing practical notes on Spring Boot, security, microservices, and cloud-native architecture.
# 1) Root DN
dn: dc=suaybsimsek,dc=com
objectClass: top
objectClass: domain
objectClass: extensibleObject
dc: suaybsimsek
# 2) People OU
dn: ou=people,dc=suaybsimsek,dc=com
objectClass: top
objectClass: organizationalUnit
ou: people
# 3) Admin user
dn: uid=admin,ou=people,dc=suaybsimsek,dc=com
objectClass: top
objectClass: person
objectClass: inetOrgPerson
cn: Admin User
sn: User
uid: admin
userPassword: $2a$10$sva6wl8pmGKJE6NIWrxwcuJK1Jaa2I/LOI43iHVpbR4YB8KjGViiK
# 4) Normal user
dn: uid=user,ou=people,dc=suaybsimsek,dc=com
objectClass: top
objectClass: person
objectClass: inetOrgPerson
cn: Normal User
sn: User
uid: user
userPassword: $2a$10$5Py4PyteLuXEqnGpSigzfu0V55C7Hi7zX18lmh.J8Bpmft.h23voG
# 5) Groups OU
dn: ou=groups,dc=suaybsimsek,dc=com
objectClass: top
objectClass: organizationalUnit
ou: groups
# 6) USER group → maps to ROLE_USER
dn: cn=USER,ou=groups,dc=suaybsimsek,dc=com
objectClass: top
objectClass: groupOfUniqueNames
cn: USER
uniqueMember: uid=user,ou=people,dc=suaybsimsek,dc=com
uniqueMember: uid=admin,ou=people,dc=suaybsimsek,dc=com
# 7) ADMIN group → maps to ROLE_ADMIN
dn: cn=ADMIN,ou=groups,dc=suaybsimsek,dc=com
objectClass: top
objectClass: groupOfUniqueNames
cn: ADMIN
uniqueMember: uid=admin,ou=people,dc=suaybsimsek,dc=com
This combined configuration ensures your application boots an embedded LDAP server with the defined schema, and loads RSA keys for JWE signing and encryption. You can now proceed to configure Spring Security to use LDAP for authentication and JWE for token issuance.
🛠️ Step 3: Security Configuration
In this section, we define the beans and properties configure LDAP authentication, set up RSA keys, and enforce HTTP security filters for a JWE-based authentication:
JwtProperties: Configures JWT issuer, expiration, and signing/encryption key pairs.
SecurityConfig: Defines embedded LDAP authentication, JWE resource server, and stateless security filter chain with route authorization.
SecurityJwtConfig
SecurityConfig
JwtProperties
🛠️ Step 4: Secure JWE Token Utilities
In this section, we define the core utility classes and constants needed to generate, encrypt, and resolve JSON Web Encryption (JWE) tokens in your Spring Boot application. These components work together to:
AuthoritiesConstants: Centralize role names with the ROLE_ prefix.
CookieBearerTokenResolver: Resolve bearer tokens from Authorization headers or HTTP cookies.
CookieUtils: Create HTTP-only, secure cookies for access tokens.
JweUtil: Sign (JWS) and encrypt (JWE) JWTs using RSA keys and Nimbus.
KeyUtils: Build RSA JWKs from PEM‐encoded key material.
SecurityUtils: Extract the current user’s login from the security context.
These utilities form the foundation for a stateless, JWE‐based authentication flow in Spring Security.
AuthoritiesConstants
CookieBearerTokenResolver
CookieUtils
JweUtil
KeyUtils
SecurityUtils
🛠️ Step 5: Authentication & Protected Endpoints
In this section, we define the REST controllers and DTOs necessary for:
AuthController: Authenticate users, issue JWE tokens, and set secure cookies.
HelloController: Expose protected resource endpoints for authenticated users and admin-specific paths.
LoginRequestDTO: Model the login request payload (username/password).
TokenDTO: Model the authentication response including token and expiration.
These components complete the stateless authentication flow by handling login, token issuance, cookie management, and resource protection.
In this section, we expose REST controllers and DTOs to handle user authentication, token issuance, and protected resource access.
AuthController
HelloController
LoginRequestDTO
TokenDTO
▶️ Run the App
BASH
./mvnw spring-boot:run
# or
gradle bootRun
🧪 Test Endpoints
In this section, we clarify Test Endpoints and summarize the key points you will apply in implementation.
Admin Flow
Login as admin and capture the JWE token from the Set-Cookie header:
You now have a practical Spring Boot LDAP and JWE Authentication implementation with a clear, production-friendly Spring Boot structure. As a next step, adapt configuration and tests to your own domain, then validate behavior under realistic traffic and failure scenarios.
package io.github.susimsek.springbootldapjwedemo.config
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration
import java.time.Duration
@Configuration
@ConfigurationProperties(prefix = "security.jwt")
class JwtProperties {
class Pair {
lateinit var publicKey: String
lateinit var privateKey: String
lateinit var keyId: String
}
lateinit var signing: Pair
lateinit var encryption: Pair
lateinit var issuer: String
lateinit var expirationDuration: Duration
}
JAVAAuthoritiesConstants.java
package io.github.susimsek.springbootldapjwedemo.security;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class AuthoritiesConstants {
public static final String ADMIN = "ROLE_ADMIN";
public static final String USER = "ROLE_USER";
public static final String ANONYMOUS = "ROLE_ANONYMOUS";
}
KOTLINAuthoritiesConstants.kt
package io.github.susimsek.springbootldapjwedemo.security
object AuthoritiesConstants {
const val ADMIN = "ROLE_ADMIN"
const val USER = "ROLE_USER"
const val ANONYMOUS = "ROLE_ANONYMOUS"
}
package io.github.susimsek.springbootldapjwedemo.controller;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.web.bind.annotation.*;
import static io.github.susimsek.springbootldapjwedemo.security.SecurityUtils.AUTHORITIES_KEY;
@RestController
@RequestMapping("/api/hello")
public class HelloController {
@GetMapping
public String helloAll(@AuthenticationPrincipal Jwt jwt) {
String user = jwt.getSubject();
var roles = jwt.getClaimAsStringList(AUTHORITIES_KEY);
return "Hello, " + user + "! Your roles: " + roles;
}
@GetMapping("/admin")
public String helloAdmin(@AuthenticationPrincipal Jwt jwt) {
return "Hello Admin, " + jwt.getSubject() + "!";
}
}
KOTLINHelloController.kt
package io.github.susimsek.springbootldapjwedemo.controller
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.web.bind.annotation.*
import io.github.susimsek.springbootldapjwedemo.security.SecurityUtils.AUTHORITIES_KEY
@RestController
@RequestMapping("/api/hello")
class HelloController {
@GetMapping
fun helloAll(@AuthenticationPrincipal jwt: Jwt): String {
val user = jwt.subject
val roles = jwt.getClaimAsStringList(AUTHORITIES_KEY)
return "Hello, \$user! Your roles: \$roles"
}
@GetMapping("/admin")
fun helloAdmin(@AuthenticationPrincipal jwt: Jwt): String {
return "Hello Admin, \${jwt.subject}!"
}
}
JAVALoginRequestDTO.java
package io.github.susimsek.springbootldapjwedemo.dto;
public record LoginRequestDTO(
String username,
String password
) { }
KOTLINLoginRequestDTO.kt
package io.github.susimsek.springbootldapjwedemo.dto
data class LoginRequestDTO(
val username: String,
val password: String
)
JAVATokenDTO.java
package io.github.susimsek.springbootldapjwedemo.dto;
public record TokenDTO(
String accessToken,
String tokenType,
long accessTokenExpiresIn
) {}
KOTLINTokenDTO.kt
package io.github.susimsek.springbootldapjwedemo.dto
import kotlin.Long
data class TokenDTO(
val accessToken: String,
val tokenType: String,
val accessTokenExpiresIn: Long
)