Spring Boot lets you combine JWS signing with JWE encryption to protect both the integrity and confidentiality of your JWTs in Spring-powered microservices.
Written by
Şuayb Şimşek
Backend-focused fullstack developer sharing practical notes on Spring Boot, security, microservices, and cloud-native architecture.
SecurityConfig: Defines in-memory users and the stateless security filter chain with route authorization.
AdminProperties
SecurityJwtConfig
SecurityConfig
JwtProperties
UserProperties
🛠️ 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 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.springbootjwedemo.config
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration
@Configuration
@ConfigurationProperties(prefix = "security.user")
class UserProperties {
lateinit var username: String
lateinit var password: String
}
JAVAAuthoritiesConstants.java
package io.github.susimsek.springbootjwedemo.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.springbootjwedemo.security
object AuthoritiesConstants {
const val ADMIN = "ROLE_ADMIN"
const val USER = "ROLE_USER"
const val ANONYMOUS = "ROLE_ANONYMOUS"
}
package io.github.susimsek.springbootjwedemo.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.springbootjwedemo.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.springbootjwedemo.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.springbootjwedemo.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.springbootjwedemo.dto;
public record LoginRequestDTO(
String username,
String password
) { }
KOTLINLoginRequestDTO.kt
package io.github.susimsek.springbootjwedemo.dto
data class LoginRequestDTO(
val username: String,
val password: String
)
JAVATokenDTO.java
package io.github.susimsek.springbootjwedemo.dto;
public record TokenDTO(
String accessToken,
String tokenType,
long accessTokenExpiresIn
) {}
KOTLINTokenDTO.kt
package io.github.susimsek.springbootjwedemo.dto
import kotlin.Long
data class TokenDTO(
val accessToken: String,
val tokenType: String,
val accessTokenExpiresIn: Long
)