xjc
@@ -107,11 +114,12 @@
com.baeldung.boot.Application
- 3.1.3
- 1.17.2
- 1.10.0
+ 4.0.10
+ 1.19.7
+ 3.3.0
1.4.6
- 0.15.1
+ 0.15.3
+ 5.4.0
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/ProductEndpoint.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/ProductEndpoint.java
index c3ba5c04a8..16b976b152 100644
--- a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/ProductEndpoint.java
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/ProductEndpoint.java
@@ -1,21 +1,23 @@
package com.baeldung.webservice;
-import com.baeldung.webservice.generated.GetProductRequest;
-import com.baeldung.webservice.generated.GetProductResponse;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
+import com.baeldung.webservice.generated.GetProductRequest;
+import com.baeldung.webservice.generated.GetProductResponse;
+
@Endpoint
public class ProductEndpoint {
private static final String NAMESPACE_URI = "http://baeldung.com/spring-boot-web-service";
- @Autowired
- private ProductRepository productRepository;
+ private final ProductRepository productRepository;
+ public ProductEndpoint(ProductRepository productRepository) {
+ this.productRepository = productRepository;
+ }
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getProductRequest")
@ResponsePayload
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/GetProductRequest.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/GetProductRequest.java
index d04302456b..f6b48af278 100644
--- a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/GetProductRequest.java
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/GetProductRequest.java
@@ -1,11 +1,11 @@
package com.baeldung.webservice.generated;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlType;
/**
@@ -13,17 +13,17 @@ import javax.xml.bind.annotation.XmlType;
*
* The following schema fragment specifies the expected content contained within this class.
*
- *
- * <complexType>
- * <complexContent>
- * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- * <sequence>
- * <element name="id" type="{http://www.w3.org/2001/XMLSchema}string"/>
- * </sequence>
- * </restriction>
- * </complexContent>
- * </complexType>
- *
+ * {@code
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * }
*
*
*/
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/GetProductResponse.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/GetProductResponse.java
index f8fcaa094f..a2360d83eb 100644
--- a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/GetProductResponse.java
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/GetProductResponse.java
@@ -1,11 +1,11 @@
package com.baeldung.webservice.generated;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlRootElement;
+import jakarta.xml.bind.annotation.XmlType;
/**
@@ -13,17 +13,17 @@ import javax.xml.bind.annotation.XmlType;
*
* The following schema fragment specifies the expected content contained within this class.
*
- *
- * <complexType>
- * <complexContent>
- * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- * <sequence>
- * <element name="product" type="{http://baeldung.com/spring-boot-web-service}product"/>
- * </sequence>
- * </restriction>
- * </complexContent>
- * </complexType>
- *
+ * {@code
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * }
*
*
*/
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/ObjectFactory.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/ObjectFactory.java
index 015ecc3f0a..e5877dd20b 100644
--- a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/ObjectFactory.java
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/ObjectFactory.java
@@ -1,7 +1,7 @@
package com.baeldung.webservice.generated;
-import javax.xml.bind.annotation.XmlRegistry;
+import jakarta.xml.bind.annotation.XmlRegistry;
/**
@@ -32,6 +32,8 @@ public class ObjectFactory {
/**
* Create an instance of {@link GetProductRequest }
*
+ * @return
+ * the new instance of {@link GetProductRequest }
*/
public GetProductRequest createGetProductRequest() {
return new GetProductRequest();
@@ -40,6 +42,8 @@ public class ObjectFactory {
/**
* Create an instance of {@link GetProductResponse }
*
+ * @return
+ * the new instance of {@link GetProductResponse }
*/
public GetProductResponse createGetProductResponse() {
return new GetProductResponse();
@@ -48,6 +52,8 @@ public class ObjectFactory {
/**
* Create an instance of {@link Product }
*
+ * @return
+ * the new instance of {@link Product }
*/
public Product createProduct() {
return new Product();
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/Product.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/Product.java
index 5957aa44b4..f1d62d1489 100644
--- a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/Product.java
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/Product.java
@@ -1,10 +1,10 @@
package com.baeldung.webservice.generated;
-import javax.xml.bind.annotation.XmlAccessType;
-import javax.xml.bind.annotation.XmlAccessorType;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlType;
+import jakarta.xml.bind.annotation.XmlAccessType;
+import jakarta.xml.bind.annotation.XmlAccessorType;
+import jakarta.xml.bind.annotation.XmlElement;
+import jakarta.xml.bind.annotation.XmlType;
/**
@@ -12,18 +12,18 @@ import javax.xml.bind.annotation.XmlType;
*
* The following schema fragment specifies the expected content contained within this class.
*
- *
- * <complexType name="product">
- * <complexContent>
- * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- * <sequence>
- * <element name="id" type="{http://www.w3.org/2001/XMLSchema}string"/>
- * <element name="name" type="{http://www.w3.org/2001/XMLSchema}string"/>
- * </sequence>
- * </restriction>
- * </complexContent>
- * </complexType>
- *
+ * {@code
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * }
*
*
*/
diff --git a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/package-info.java b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/package-info.java
index 298ae9374b..fa8f2aeec5 100644
--- a/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/package-info.java
+++ b/spring-boot-modules/spring-boot-testing-2/src/main/java/com/baeldung/webservice/generated/package-info.java
@@ -1,2 +1,2 @@
-@javax.xml.bind.annotation.XmlSchema(namespace = "http://baeldung.com/spring-boot-web-service", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
+@jakarta.xml.bind.annotation.XmlSchema(namespace = "http://baeldung.com/spring-boot-web-service", elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.baeldung.webservice.generated;
diff --git a/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/keycloaktestcontainers/KeycloakTestContainers.java b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/keycloaktestcontainers/KeycloakTestContainers.java
index 2a50a646c5..d68fae9cb5 100644
--- a/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/keycloaktestcontainers/KeycloakTestContainers.java
+++ b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/keycloaktestcontainers/KeycloakTestContainers.java
@@ -4,8 +4,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
-import javax.annotation.PostConstruct;
-
import org.apache.http.client.utils.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -23,6 +21,7 @@ import org.springframework.web.reactive.function.client.WebClient;
import dasniko.testcontainers.keycloak.KeycloakContainer;
import io.restassured.RestAssured;
+import jakarta.annotation.PostConstruct;
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public abstract class KeycloakTestContainers {
diff --git a/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/webservice/ProductEndpointIntegrationTest.java b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/webservice/ProductEndpointIntegrationTest.java
index edd15090b8..0d58cfde17 100644
--- a/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/webservice/ProductEndpointIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-testing-2/src/test/java/com/baeldung/webservice/ProductEndpointIntegrationTest.java
@@ -15,6 +15,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.webservices.server.WebServiceServerTest;
import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.test.server.MockWebServiceClient;
import org.springframework.xml.transform.StringSource;
@@ -22,6 +23,7 @@ import org.springframework.xml.transform.StringSource;
import com.baeldung.webservice.generated.Product;
@WebServiceServerTest
+@ComponentScan("com.baeldung.webservice")
class ProductEndpointIntegrationTest {
private static final Map NAMESPACE_MAPPING = createMapping();
diff --git a/spring-boot-modules/spring-boot-testing/pom.xml b/spring-boot-modules/spring-boot-testing/pom.xml
index 7643183fcb..28ce90d8ec 100644
--- a/spring-boot-modules/spring-boot-testing/pom.xml
+++ b/spring-boot-modules/spring-boot-testing/pom.xml
@@ -50,7 +50,7 @@
- it.ozimov
+ com.github.codemonstur
embedded-redis
${embedded-redis.version}
test
@@ -114,7 +114,7 @@
2.2.4
2.4-M1-groovy-4.0
3.0.0
- 0.7.2
+ 1.4.2
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/boot/embeddedRedis/TestRedisConfiguration.java b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/boot/embeddedRedis/TestRedisConfiguration.java
index 10e5d56857..f0ac4be194 100644
--- a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/boot/embeddedRedis/TestRedisConfiguration.java
+++ b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/boot/embeddedRedis/TestRedisConfiguration.java
@@ -1,18 +1,21 @@
package com.baeldung.boot.embeddedRedis;
-import com.baeldung.boot.embeddedRedis.configuration.RedisProperties;
+import java.io.IOException;
+
import org.springframework.boot.test.context.TestConfiguration;
-import redis.embedded.RedisServer;
+
+import com.baeldung.boot.embeddedRedis.configuration.RedisProperties;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
+import redis.embedded.RedisServer;
@TestConfiguration
public class TestRedisConfiguration {
private final RedisServer redisServer;
- public TestRedisConfiguration(final RedisProperties redisProperties) {
+ public TestRedisConfiguration(final RedisProperties redisProperties) throws IOException {
this.redisServer = new RedisServer(redisProperties.getRedisPort());
//Uncomment below if running on windows and can't start redis server
// this.redisServer = RedisServer.builder().setting("maxheap 200m").port(6379).setting("bind localhost").build();
@@ -20,12 +23,12 @@ public class TestRedisConfiguration {
}
@PostConstruct
- public void postConstruct() {
+ public void postConstruct() throws IOException {
redisServer.start();
}
@PreDestroy
- public void preDestroy() {
+ public void preDestroy() throws IOException {
redisServer.stop();
}
}
diff --git a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/boot/embeddedRedis/domain/repository/UserRepositoryIntegrationTest.java b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/boot/embeddedRedis/domain/repository/UserRepositoryIntegrationTest.java
index 9577ccf0e8..331801cc0a 100644
--- a/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/boot/embeddedRedis/domain/repository/UserRepositoryIntegrationTest.java
+++ b/spring-boot-modules/spring-boot-testing/src/test/java/com/baeldung/boot/embeddedRedis/domain/repository/UserRepositoryIntegrationTest.java
@@ -1,26 +1,25 @@
package com.baeldung.boot.embeddedRedis.domain.repository;
-import com.baeldung.boot.embeddedRedis.TestRedisConfiguration;
-import com.baeldung.boot.embeddedRedis.domain.User;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.util.UUID;
-import static org.junit.Assert.assertNotNull;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import com.baeldung.boot.embeddedRedis.TestRedisConfiguration;
+import com.baeldung.boot.embeddedRedis.domain.User;
+
-@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestRedisConfiguration.class)
-public class UserRepositoryIntegrationTest {
+class UserRepositoryIntegrationTest {
@Autowired
private UserRepository userRepository;
@Test
- public void shouldSaveUser_toRedis() {
+ void shouldSaveUser_toRedis() {
final UUID id = UUID.randomUUID();
final User user = new User(id, "name");
diff --git a/spring-boot-modules/spring-caching-2/README.md b/spring-boot-modules/spring-caching-2/README.md
index 864a17d98b..fffdacdfb2 100644
--- a/spring-boot-modules/spring-caching-2/README.md
+++ b/spring-boot-modules/spring-caching-2/README.md
@@ -2,4 +2,5 @@
- [Spring Boot Cache with Redis](https://www.baeldung.com/spring-boot-redis-cache)
- [Setting Time-To-Live Value for Caching](https://www.baeldung.com/spring-setting-ttl-value-cache)
- [Get All Cached Keys with Caffeine Cache in Spring Boot](https://www.baeldung.com/spring-boot-caffeine-spring-get-all-keys)
+- [Implement Two-Level Cache With Spring](https://www.baeldung.com/spring-two-level-cache)
diff --git a/spring-boot-modules/spring-caching-2/pom.xml b/spring-boot-modules/spring-caching-2/pom.xml
index ec9215aa32..95fae4a0ef 100644
--- a/spring-boot-modules/spring-caching-2/pom.xml
+++ b/spring-boot-modules/spring-caching-2/pom.xml
@@ -51,7 +51,7 @@
${caffeine.version}
- it.ozimov
+ com.github.codemonstur
embedded-redis
${embedded.redis.version}
@@ -65,7 +65,7 @@
- 0.7.3
+ 1.4.0
3.1.8
com.baeldung.caching.ttl.CachingTTLApplication
diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CacheConfig.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CacheConfig.java
new file mode 100644
index 0000000000..576bcd97ab
--- /dev/null
+++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CacheConfig.java
@@ -0,0 +1,71 @@
+package com.baeldung.caching.twolevelcache;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.AnnotationCacheOperationSource;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.caffeine.CaffeineCache;
+import org.springframework.cache.interceptor.CacheInterceptor;
+import org.springframework.cache.interceptor.CacheOperationSource;
+import org.springframework.cache.support.SimpleCacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+
+import java.time.Duration;
+import java.util.Arrays;
+
+@Configuration
+@EnableCaching
+public class CacheConfig {
+
+ @Bean
+ @Primary
+ public CacheManager caffeineCacheManager(CaffeineCache caffeineCache) {
+ SimpleCacheManager manager = new SimpleCacheManager();
+ manager.setCaches(Arrays.asList(caffeineCache));
+ return manager;
+ }
+
+ @Bean
+ public CaffeineCache caffeineCacheConfig() {
+ return new CaffeineCache("customerCache", Caffeine.newBuilder()
+ .expireAfterWrite(Duration.ofSeconds(3))
+ .initialCapacity(1)
+ .maximumSize(2000)
+ .build());
+ }
+
+ @Bean
+ public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory, RedisCacheConfiguration redisCacheConfiguration) {
+ return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory)
+ .withCacheConfiguration("customerCache", redisCacheConfiguration)
+ .build();
+ }
+
+ @Bean
+ public RedisCacheConfiguration cacheConfiguration() {
+ return RedisCacheConfiguration.defaultCacheConfig()
+ .entryTtl(Duration.ofMinutes(5))
+ .disableCachingNullValues()
+ .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
+ }
+
+ @Bean
+ public CacheInterceptor cacheInterceptor(CacheManager caffeineCacheManager, CacheOperationSource cacheOperationSource) {
+ CacheInterceptor interceptor = new CustomerCacheInterceptor(caffeineCacheManager);
+ interceptor.setCacheOperationSources(cacheOperationSource);
+ return interceptor;
+ }
+
+ @Bean
+ public CacheOperationSource cacheOperationSource() {
+ return new AnnotationCacheOperationSource();
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/Customer.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/Customer.java
new file mode 100644
index 0000000000..0b985d35f3
--- /dev/null
+++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/Customer.java
@@ -0,0 +1,25 @@
+package com.baeldung.caching.twolevelcache;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.Id;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+@Data
+@Entity
+@AllArgsConstructor
+@NoArgsConstructor
+@Setter
+public class Customer implements Serializable {
+
+ @Id
+ private String id;
+
+ private String name;
+
+ private String email;
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerCacheInterceptor.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerCacheInterceptor.java
new file mode 100644
index 0000000000..f1a8dca0db
--- /dev/null
+++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerCacheInterceptor.java
@@ -0,0 +1,29 @@
+package com.baeldung.caching.twolevelcache;
+
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.interceptor.CacheInterceptor;
+import org.springframework.data.redis.cache.RedisCache;
+
+public class CustomerCacheInterceptor extends CacheInterceptor {
+
+ private final CacheManager caffeineCacheManager;
+
+ public CustomerCacheInterceptor(CacheManager caffeineCacheManager) {
+ this.caffeineCacheManager = caffeineCacheManager;
+ }
+
+ @Override
+ protected Cache.ValueWrapper doGet(Cache cache, Object key) {
+ Cache.ValueWrapper existingCacheValue = super.doGet(cache, key);
+
+ if (existingCacheValue != null && cache.getClass() == RedisCache.class) {
+ Cache caffeineCache = caffeineCacheManager.getCache(cache.getName());
+ if (caffeineCache != null) {
+ caffeineCache.putIfAbsent(key, existingCacheValue.get());
+ }
+ }
+
+ return existingCacheValue;
+ }
+}
diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerRepository.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerRepository.java
new file mode 100644
index 0000000000..098112f152
--- /dev/null
+++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerRepository.java
@@ -0,0 +1,6 @@
+package com.baeldung.caching.twolevelcache;
+
+import org.springframework.data.repository.CrudRepository;
+
+public interface CustomerRepository extends CrudRepository {
+}
diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerService.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerService.java
new file mode 100644
index 0000000000..088b66919c
--- /dev/null
+++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/CustomerService.java
@@ -0,0 +1,26 @@
+package com.baeldung.caching.twolevelcache;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.Caching;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CustomerService {
+
+ private final CustomerRepository customerRepository;
+
+ @Autowired
+ public CustomerService(CustomerRepository customerRepository) {
+ this.customerRepository = customerRepository;
+ }
+
+ @Caching(cacheable = {
+ @Cacheable(cacheNames = "customerCache", cacheManager = "caffeineCacheManager"),
+ @Cacheable(cacheNames = "customerCache", cacheManager = "redisCacheManager")
+ })
+ public Customer getCustomer(String id) {
+ return customerRepository.findById(id)
+ .orElseThrow(RuntimeException::new);
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/TwoLevelCacheApplication.java b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/TwoLevelCacheApplication.java
new file mode 100644
index 0000000000..9bc2c65e6e
--- /dev/null
+++ b/spring-boot-modules/spring-caching-2/src/main/java/com/baeldung/caching/twolevelcache/TwoLevelCacheApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.caching.twolevelcache;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TwoLevelCacheApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TwoLevelCacheApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-caching-2/src/main/resources/application.properties b/spring-boot-modules/spring-caching-2/src/main/resources/application.properties
index 38f3537d01..49bd715e43 100644
--- a/spring-boot-modules/spring-caching-2/src/main/resources/application.properties
+++ b/spring-boot-modules/spring-caching-2/src/main/resources/application.properties
@@ -11,3 +11,4 @@ caching.spring.hotelListTTL=43200
# Connection details
#spring.redis.host=localhost
#spring.redis.port=6379
+spring.main.allow-bean-definition-overriding=true
diff --git a/spring-boot-modules/spring-caching-2/src/test/java/com/baeldung/caching/redis/ItemServiceCachingIntegrationTest.java b/spring-boot-modules/spring-caching-2/src/test/java/com/baeldung/caching/redis/ItemServiceCachingIntegrationTest.java
index 8868edb74f..01740ba780 100644
--- a/spring-boot-modules/spring-caching-2/src/test/java/com/baeldung/caching/redis/ItemServiceCachingIntegrationTest.java
+++ b/spring-boot-modules/spring-caching-2/src/test/java/com/baeldung/caching/redis/ItemServiceCachingIntegrationTest.java
@@ -5,8 +5,6 @@ import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import java.util.Optional;
-
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -20,10 +18,12 @@ import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit.jupiter.SpringExtension;
+import redis.embedded.RedisServer;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
-import redis.embedded.RedisServer;
+import java.io.IOException;
+import java.util.Optional;
@Import({ CacheConfig.class, ItemService.class })
@ExtendWith(SpringExtension.class)
@@ -69,17 +69,17 @@ class ItemServiceCachingIntegrationTest {
private final RedisServer redisServer;
- public EmbeddedRedisConfiguration() {
+ public EmbeddedRedisConfiguration() throws IOException {
this.redisServer = new RedisServer();
}
@PostConstruct
- public void startRedis() {
+ public void startRedis() throws IOException {
redisServer.start();
}
@PreDestroy
- public void stopRedis() {
+ public void stopRedis() throws IOException {
this.redisServer.stop();
}
}
diff --git a/spring-boot-modules/spring-caching-2/src/test/java/com/baeldung/caching/twolevelcache/CustomerServiceCachingIntegrationTest.java b/spring-boot-modules/spring-caching-2/src/test/java/com/baeldung/caching/twolevelcache/CustomerServiceCachingIntegrationTest.java
new file mode 100644
index 0000000000..3db53198fc
--- /dev/null
+++ b/spring-boot-modules/spring-caching-2/src/test/java/com/baeldung/caching/twolevelcache/CustomerServiceCachingIntegrationTest.java
@@ -0,0 +1,124 @@
+package com.baeldung.caching.twolevelcache;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
+import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import redis.embedded.RedisServer;
+
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import java.io.IOException;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+
+@Import({ CacheConfig.class,CustomerService.class })
+@ExtendWith(SpringExtension.class)
+@ImportAutoConfiguration(classes = { CacheAutoConfiguration.class, RedisAutoConfiguration.class })
+@EnableCaching
+class CustomerServiceCachingIntegrationTest {
+
+ @MockBean
+ private CustomerRepository customerRepository;
+
+ @Autowired
+ private CustomerService customerService;
+
+ @Autowired
+ private CacheManager redisCacheManager;
+
+ @Autowired
+ private CacheManager caffeineCacheManager;
+
+ @Test
+ void givenCustomerIsPresent_whenGetCustomerCalled_thenReturnCustomerAndCacheIt() {
+ String CUSTOMER_ID = "100";
+ Customer customer = new Customer(CUSTOMER_ID, "test", "test@mail.com");
+
+ given(customerRepository.findById(CUSTOMER_ID)).willReturn(Optional.of(customer));
+
+ Customer customerCacheMiss = customerService.getCustomer(CUSTOMER_ID);
+
+ assertThat(customerCacheMiss).isEqualTo(customer);
+ verify(customerRepository, times(1)).findById(CUSTOMER_ID);
+ assertThat(customerFromCaffeineCache(CUSTOMER_ID)).isEqualTo(customer);
+ assertThat(customerFromRedisCache(CUSTOMER_ID)).isEqualTo(customer);
+ }
+
+ @Test
+ void givenCustomerIsPresent_whenGetCustomerCalledTwice_thenReturnCustomerAndCacheIt() {
+ String CUSTOMER_ID = "101";
+ Customer customer = new Customer(CUSTOMER_ID, "test", "test@mail.com");
+ given(customerRepository.findById(CUSTOMER_ID)).willReturn(Optional.of(customer));
+
+ Customer customerCacheMiss = customerService.getCustomer(CUSTOMER_ID);
+ Customer customerCacheHit = customerService.getCustomer(CUSTOMER_ID);
+
+ assertThat(customerCacheMiss).isEqualTo(customer);
+ assertThat(customerCacheHit).isEqualTo(customer);
+ verify(customerRepository, times(1)).findById(CUSTOMER_ID);
+ assertThat(customerFromCaffeineCache(CUSTOMER_ID)).isEqualTo(customer);
+ assertThat(customerFromRedisCache(CUSTOMER_ID)).isEqualTo(customer);
+ }
+
+ @Test
+ void givenCustomerIsPresent_whenGetCustomerCalledTwiceAndFirstCacheExpired_thenReturnCustomerAndCacheIt() throws InterruptedException {
+ String CUSTOMER_ID = "102";
+ Customer customer = new Customer(CUSTOMER_ID, "test", "test@mail.com");
+ given(customerRepository.findById(CUSTOMER_ID)).willReturn(Optional.of(customer));
+
+ Customer customerCacheMiss = customerService.getCustomer(CUSTOMER_ID);
+ TimeUnit.SECONDS.sleep(3);
+ assertThat(customerFromCaffeineCache(CUSTOMER_ID)).isEqualTo(null);
+ Customer customerCacheHit = customerService.getCustomer(CUSTOMER_ID);
+
+ verify(customerRepository, times(1)).findById(CUSTOMER_ID);
+ assertThat(customerCacheMiss).isEqualTo(customer);
+ assertThat(customerCacheHit).isEqualTo(customer);
+ assertThat(customerFromCaffeineCache(CUSTOMER_ID)).isEqualTo(customer);
+ assertThat(customerFromRedisCache(CUSTOMER_ID)).isEqualTo(customer);
+ }
+
+ private Object customerFromRedisCache(String key) {
+ return redisCacheManager.getCache("customerCache").get(key) != null ?
+ redisCacheManager.getCache("customerCache").get(key).get() : null;
+ }
+
+ private Object customerFromCaffeineCache(String key) {
+ return caffeineCacheManager.getCache("customerCache").get(key) != null ?
+ caffeineCacheManager.getCache("customerCache").get(key).get() : null;
+ }
+
+ @TestConfiguration
+ static class EmbeddedRedisConfiguration {
+
+ private final RedisServer redisServer;
+
+ public EmbeddedRedisConfiguration() throws IOException {
+ this.redisServer = new RedisServer();
+ }
+
+ @PostConstruct
+ public void startRedis() throws IOException {
+ redisServer.start();
+ }
+
+ @PreDestroy
+ public void stopRedis() throws IOException {
+ this.redisServer.stop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/spring-credhub/pom.xml b/spring-credhub/pom.xml
index f257d549bf..0065269bc2 100644
--- a/spring-credhub/pom.xml
+++ b/spring-credhub/pom.xml
@@ -10,9 +10,9 @@
com.baeldung
- parent-boot-2
+ parent-boot-3
0.0.1-SNAPSHOT
- ../parent-boot-2
+ ../parent-boot-3
@@ -37,8 +37,7 @@
- 2.2.0
- UTF-8
+ 3.1.0
\ No newline at end of file
diff --git a/spring-di-2/src/main/java/com/baeldung/circulardependency/CircularDependencyB.java b/spring-di-2/src/main/java/com/baeldung/circulardependency/CircularDependencyB.java
index dc2240d0b5..4ebb113651 100644
--- a/spring-di-2/src/main/java/com/baeldung/circulardependency/CircularDependencyB.java
+++ b/spring-di-2/src/main/java/com/baeldung/circulardependency/CircularDependencyB.java
@@ -1,6 +1,7 @@
package com.baeldung.circulardependency;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
@Component
@@ -11,7 +12,7 @@ public class CircularDependencyB {
private String message = "Hi!";
@Autowired
- public void setCircA(final CircularDependencyA circA) {
+ public void setCircA(@Lazy final CircularDependencyA circA) {
this.circA = circA;
}
diff --git a/spring-di-4/pom.xml b/spring-di-4/pom.xml
index 1eec8efcf0..a486b19e51 100644
--- a/spring-di-4/pom.xml
+++ b/spring-di-4/pom.xml
@@ -24,5 +24,15 @@
spring-boot-starter-test
-
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ com.baeldung.registrypostprocessor.RegistryPostProcessorApplication
+
+
+
+
\ No newline at end of file
diff --git a/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/ApiClientConfiguration.java b/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/ApiClientConfiguration.java
new file mode 100644
index 0000000000..5faf5d2707
--- /dev/null
+++ b/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/ApiClientConfiguration.java
@@ -0,0 +1,41 @@
+package com.baeldung.registrypostprocessor;
+
+import com.baeldung.registrypostprocessor.bean.ApiClient;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+import org.springframework.boot.context.properties.bind.Bindable;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.core.env.Environment;
+
+import java.util.HashMap;
+import java.util.List;
+
+public class ApiClientConfiguration implements BeanDefinitionRegistryPostProcessor {
+ private static final String API_CLIENT_BEAN_NAME = "apiClient_";
+ List clients;
+
+ public ApiClientConfiguration(Environment environment) {
+ Binder binder = Binder.get(environment);
+ List properties = binder.bind("api.clients", Bindable.listOf(HashMap.class)).get();
+ clients = properties.stream().map(client -> new ApiClient(String.valueOf(client.get("name")),
+ String.valueOf(client.get("url")), String.valueOf(client.get("key")))).toList();
+ }
+
+ @Override
+ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
+ clients.forEach(client -> {
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ApiClient.class);
+ builder.addPropertyValue("name", client.getName());
+ builder.addPropertyValue("url", client.getUrl());
+ builder.addPropertyValue("key", client.getKey());
+ registry.registerBeanDefinition(API_CLIENT_BEAN_NAME + client.getName(), builder.getBeanDefinition());
+ });
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ }
+}
\ No newline at end of file
diff --git a/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/RegistryPostProcessorApplication.java b/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/RegistryPostProcessorApplication.java
new file mode 100644
index 0000000000..311f72e0fa
--- /dev/null
+++ b/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/RegistryPostProcessorApplication.java
@@ -0,0 +1,22 @@
+package com.baeldung.registrypostprocessor;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.env.ConfigurableEnvironment;
+
+
+@SpringBootApplication
+public class RegistryPostProcessorApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(RegistryPostProcessorApplication.class, args);
+ }
+
+ @Bean
+ public ApiClientConfiguration apiClientConfiguration(ConfigurableEnvironment environment) {
+ return new ApiClientConfiguration(environment);
+ }
+
+}
+
diff --git a/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/bean/ApiClient.java b/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/bean/ApiClient.java
new file mode 100644
index 0000000000..00d7f58a9a
--- /dev/null
+++ b/spring-di-4/src/main/java/com/baeldung/registrypostprocessor/bean/ApiClient.java
@@ -0,0 +1,45 @@
+package com.baeldung.registrypostprocessor.bean;
+
+public class ApiClient {
+ private String name;
+ private String url;
+ private String key;
+
+ public ApiClient(String name, String url, String key) {
+ this.name = name;
+ this.url = url;
+ this.key = key;
+ }
+
+ public ApiClient() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getConnectionProperties() {
+ return "Connecting to " + name + " at " + url;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-di-4/src/main/resources/application.yml b/spring-di-4/src/main/resources/application.yml
index 5c09fdb8b0..51d20e5b40 100644
--- a/spring-di-4/src/main/resources/application.yml
+++ b/spring-di-4/src/main/resources/application.yml
@@ -1 +1,9 @@
-ambiguous-bean: 'A'
\ No newline at end of file
+ambiguous-bean: 'A'
+api:
+ clients:
+ - name: example
+ url: https://api.example.com
+ key: 12345
+ - name: anotherexample
+ url: https://api.anotherexample.com
+ key: 67890
\ No newline at end of file
diff --git a/spring-di-4/src/test/java/com/baeldung/registrypostprocessor/ApiClientConfigurationUnitTest.java b/spring-di-4/src/test/java/com/baeldung/registrypostprocessor/ApiClientConfigurationUnitTest.java
new file mode 100644
index 0000000000..80e578166c
--- /dev/null
+++ b/spring-di-4/src/test/java/com/baeldung/registrypostprocessor/ApiClientConfigurationUnitTest.java
@@ -0,0 +1,23 @@
+package com.baeldung.registrypostprocessor;
+
+import com.baeldung.registrypostprocessor.bean.ApiClient;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ApplicationContext;
+
+@SpringBootTest(classes = RegistryPostProcessorApplication.class)
+public class ApiClientConfigurationUnitTest {
+ @Autowired
+ private ApplicationContext context;
+
+ @Test
+ void givenBeansRegistered_whenConnect_thenConnected() {
+ ApiClient exampleClient = (ApiClient) context.getBean("apiClient_example");
+ Assertions.assertEquals("Connecting to example at https://api.example.com", exampleClient.getConnectionProperties());
+
+ ApiClient anotherExampleClient = (ApiClient) context.getBean("apiClient_anotherexample");
+ Assertions.assertEquals("Connecting to anotherexample at https://api.anotherexample.com", anotherExampleClient.getConnectionProperties());
+ }
+}
\ No newline at end of file
diff --git a/spring-di-4/src/test/resources/application.yml b/spring-di-4/src/test/resources/application.yml
index da23e59c24..09ec2c0921 100644
--- a/spring-di-4/src/test/resources/application.yml
+++ b/spring-di-4/src/test/resources/application.yml
@@ -1 +1,9 @@
-ambiguous-bean: 'B'
\ No newline at end of file
+ambiguous-bean: 'B'
+api:
+ clients:
+ - name: example
+ url: https://api.example.com
+ apiKey: 12345
+ - name: anotherexample
+ url: https://api.anotherexample.com
+ apiKey: 67890
\ No newline at end of file
diff --git a/spring-kafka-2/README.md b/spring-kafka-2/README.md
index 4dff7ef5db..7dfb05d160 100644
--- a/spring-kafka-2/README.md
+++ b/spring-kafka-2/README.md
@@ -10,3 +10,4 @@ This module contains articles about Spring with Kafka
- [Splitting Streams in Kafka](https://www.baeldung.com/kafka-splitting-streams)
- [Manage Kafka Consumer Groups](https://www.baeldung.com/kafka-manage-consumer-groups)
- [Dead Letter Queue for Kafka With Spring](https://www.baeldung.com/kafka-spring-dead-letter-queue)
+- [Configuring Kafka SSL Using Spring Boot](https://www.baeldung.com/spring-boot-kafka-ssl)
diff --git a/spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaConsumer.java b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaConsumer.java
similarity index 94%
rename from spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaConsumer.java
rename to spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaConsumer.java
index 77df74b6c9..68353312a9 100644
--- a/spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaConsumer.java
+++ b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaConsumer.java
@@ -1,12 +1,13 @@
-package com.baeldung.kafka.ssl;
+package com.baeldung.spring.kafka.ssl;
+
+import java.util.ArrayList;
+import java.util.List;
-import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
-import java.util.ArrayList;
-import java.util.List;
+import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
diff --git a/spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaProducer.java b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaProducer.java
similarity index 94%
rename from spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaProducer.java
rename to spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaProducer.java
index 38ce366355..daf5888db0 100644
--- a/spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaProducer.java
+++ b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaProducer.java
@@ -1,9 +1,10 @@
-package com.baeldung.kafka.ssl;
+package com.baeldung.spring.kafka.ssl;
+
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Component;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.kafka.core.KafkaTemplate;
-import org.springframework.stereotype.Component;
@Slf4j
@AllArgsConstructor
diff --git a/spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaSslApplication.java b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaSslApplication.java
similarity index 74%
rename from spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaSslApplication.java
rename to spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaSslApplication.java
index b7747ebfef..4e3e4701b1 100644
--- a/spring-kafka/src/main/java/com/baeldung/kafka/ssl/KafkaSslApplication.java
+++ b/spring-kafka-2/src/main/java/com/baeldung/spring/kafka/ssl/KafkaSslApplication.java
@@ -1,7 +1,6 @@
-package com.baeldung.kafka.ssl;
+package com.baeldung.spring.kafka.ssl;
import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
diff --git a/spring-kafka/src/main/resources/application-ssl.yml b/spring-kafka-2/src/main/resources/application-ssl.yml
similarity index 100%
rename from spring-kafka/src/main/resources/application-ssl.yml
rename to spring-kafka-2/src/main/resources/application-ssl.yml
diff --git a/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/dlt/KafkaDltIntegrationTest.java b/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/dlt/KafkaDltManualTest.java
similarity index 99%
rename from spring-kafka-2/src/test/java/com/baeldung/spring/kafka/dlt/KafkaDltIntegrationTest.java
rename to spring-kafka-2/src/test/java/com/baeldung/spring/kafka/dlt/KafkaDltManualTest.java
index 72b77c360f..fb75e56063 100644
--- a/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/dlt/KafkaDltIntegrationTest.java
+++ b/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/dlt/KafkaDltManualTest.java
@@ -34,7 +34,7 @@ import org.springframework.test.context.ActiveProfiles;
topics = {"payments-fail-on-error-dlt", "payments-retry-on-error-dlt", "payments-no-dlt"}
)
@ActiveProfiles("dlt")
-public class KafkaDltIntegrationTest {
+public class KafkaDltManualTest {
private static final String FAIL_ON_ERROR_TOPIC = "payments-fail-on-error-dlt";
private static final String RETRY_ON_ERROR_TOPIC = "payments-retry-on-error-dlt";
private static final String NO_DLT_TOPIC = "payments-no-dlt";
diff --git a/spring-kafka/src/test/java/com/baeldung/kafka/ssl/KafkaSslApplicationLiveTest.java b/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/ssl/KafkaSslApplicationLiveTest.java
similarity index 94%
rename from spring-kafka/src/test/java/com/baeldung/kafka/ssl/KafkaSslApplicationLiveTest.java
rename to spring-kafka-2/src/test/java/com/baeldung/spring/kafka/ssl/KafkaSslApplicationLiveTest.java
index e05298face..d0fdc0d8cc 100644
--- a/spring-kafka/src/test/java/com/baeldung/kafka/ssl/KafkaSslApplicationLiveTest.java
+++ b/spring-kafka-2/src/test/java/com/baeldung/spring/kafka/ssl/KafkaSslApplicationLiveTest.java
@@ -1,6 +1,13 @@
-package com.baeldung.kafka.ssl;
+package com.baeldung.spring.kafka.ssl;
+
+import static com.baeldung.spring.kafka.ssl.KafkaConsumer.TOPIC;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+
+import java.io.File;
+import java.time.Duration;
+import java.util.UUID;
-import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -10,13 +17,7 @@ import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
-import java.io.File;
-import java.time.Duration;
-import java.util.UUID;
-
-import static com.baeldung.kafka.ssl.KafkaConsumer.TOPIC;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.awaitility.Awaitility.await;
+import lombok.extern.slf4j.Slf4j;
@Slf4j
@ActiveProfiles("ssl")
diff --git a/spring-kafka/src/test/resources/client-certs/kafka.client.keystore.jks b/spring-kafka-2/src/test/resources/client-certs/kafka.client.keystore.jks
similarity index 100%
rename from spring-kafka/src/test/resources/client-certs/kafka.client.keystore.jks
rename to spring-kafka-2/src/test/resources/client-certs/kafka.client.keystore.jks
diff --git a/spring-kafka/src/test/resources/client-certs/kafka.client.truststore.jks b/spring-kafka-2/src/test/resources/client-certs/kafka.client.truststore.jks
similarity index 100%
rename from spring-kafka/src/test/resources/client-certs/kafka.client.truststore.jks
rename to spring-kafka-2/src/test/resources/client-certs/kafka.client.truststore.jks
diff --git a/spring-kafka/src/test/resources/docker/certs/kafka.server.keystore.jks b/spring-kafka-2/src/test/resources/docker/certs/kafka.server.keystore.jks
similarity index 100%
rename from spring-kafka/src/test/resources/docker/certs/kafka.server.keystore.jks
rename to spring-kafka-2/src/test/resources/docker/certs/kafka.server.keystore.jks
diff --git a/spring-kafka/src/test/resources/docker/certs/kafka.server.truststore.jks b/spring-kafka-2/src/test/resources/docker/certs/kafka.server.truststore.jks
similarity index 100%
rename from spring-kafka/src/test/resources/docker/certs/kafka.server.truststore.jks
rename to spring-kafka-2/src/test/resources/docker/certs/kafka.server.truststore.jks
diff --git a/spring-kafka/src/test/resources/docker/certs/kafka_keystore_credentials b/spring-kafka-2/src/test/resources/docker/certs/kafka_keystore_credentials
similarity index 100%
rename from spring-kafka/src/test/resources/docker/certs/kafka_keystore_credentials
rename to spring-kafka-2/src/test/resources/docker/certs/kafka_keystore_credentials
diff --git a/spring-kafka/src/test/resources/docker/certs/kafka_sslkey_credentials b/spring-kafka-2/src/test/resources/docker/certs/kafka_sslkey_credentials
similarity index 100%
rename from spring-kafka/src/test/resources/docker/certs/kafka_sslkey_credentials
rename to spring-kafka-2/src/test/resources/docker/certs/kafka_sslkey_credentials
diff --git a/spring-kafka/src/test/resources/docker/certs/kafka_truststore_credentials b/spring-kafka-2/src/test/resources/docker/certs/kafka_truststore_credentials
similarity index 100%
rename from spring-kafka/src/test/resources/docker/certs/kafka_truststore_credentials
rename to spring-kafka-2/src/test/resources/docker/certs/kafka_truststore_credentials
diff --git a/spring-kafka/src/test/resources/docker/docker-compose.yml b/spring-kafka-2/src/test/resources/docker/docker-compose.yml
similarity index 100%
rename from spring-kafka/src/test/resources/docker/docker-compose.yml
rename to spring-kafka-2/src/test/resources/docker/docker-compose.yml
diff --git a/spring-kafka-3/README.md b/spring-kafka-3/README.md
index e150413789..624c6ff790 100644
--- a/spring-kafka-3/README.md
+++ b/spring-kafka-3/README.md
@@ -4,3 +4,4 @@
- [View Kafka Headers in Java](https://www.baeldung.com/java-kafka-view-headers)
- [Understanding Kafka InstanceAlreadyExistsException in Java](https://www.baeldung.com/kafka-instancealreadyexistsexception)
- [Difference Between GroupId and ConsumerId in Apache Kafka](https://www.baeldung.com/apache-kafka-groupid-vs-consumerid)
+- [Dynamically Managing Kafka Listeners in Spring Boot](https://www.baeldung.com/kafka-spring-boot-dynamically-manage-listeners)
diff --git a/spring-kafka-3/pom.xml b/spring-kafka-3/pom.xml
index 894eab2576..f94ce7748b 100644
--- a/spring-kafka-3/pom.xml
+++ b/spring-kafka-3/pom.xml
@@ -2,9 +2,9 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
com.baeldung
- parent-boot-2
+ parent-boot-3
0.0.1-SNAPSHOT
- ../parent-boot-2
+ ../parent-boot-3
4.0.0
@@ -22,6 +22,7 @@
org.springframework.kafka
spring-kafka
+ ${spring-kafka.version}
com.fasterxml.jackson.core
@@ -54,8 +55,9 @@
17
- 3.0.12
+ 3.1.2
1.19.3
4.2.0
+ org.springframework.boot.SpringApplication.Application
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/deserialization/exception/KafkaErrorHandler.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/deserialization/exception/KafkaErrorHandler.java
index ea4211ab53..99d676e6c5 100644
--- a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/deserialization/exception/KafkaErrorHandler.java
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/deserialization/exception/KafkaErrorHandler.java
@@ -13,8 +13,9 @@ class KafkaErrorHandler implements CommonErrorHandler {
private static final Logger log = LoggerFactory.getLogger(KafkaErrorHandler.class);
@Override
- public void handleRecord(Exception exception, ConsumerRecord, ?> record, Consumer, ?> consumer, MessageListenerContainer container) {
+ public boolean handleOne(Exception exception, ConsumerRecord, ?> record, Consumer, ?> consumer, MessageListenerContainer container) {
handle(exception, consumer);
+ return true;
}
@Override
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/Constants.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/Constants.java
new file mode 100644
index 0000000000..1b9f51f8a1
--- /dev/null
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/Constants.java
@@ -0,0 +1,6 @@
+package com.baeldung.spring.kafka.startstopconsumer;
+
+public class Constants {
+ public static final String MULTI_PARTITION_TOPIC = "multi_partition_topic";
+ public static final String LISTENER_ID = "listener-id-1";
+}
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/KafkaConsumerConfig.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/KafkaConsumerConfig.java
new file mode 100644
index 0000000000..d37345d2de
--- /dev/null
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/KafkaConsumerConfig.java
@@ -0,0 +1,42 @@
+package com.baeldung.spring.kafka.startstopconsumer;
+
+import org.apache.kafka.clients.consumer.ConsumerConfig;
+import org.apache.kafka.common.serialization.StringDeserializer;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.kafka.annotation.EnableKafka;
+import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
+import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
+import org.springframework.kafka.support.serializer.JsonDeserializer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@EnableKafka
+@Configuration
+public class KafkaConsumerConfig {
+
+ @Value("${spring.kafka.bootstrap-servers}")
+ private String bootstrapServers;
+
+ @Bean
+ public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() {
+ ConcurrentKafkaListenerContainerFactory factory =
+ new ConcurrentKafkaListenerContainerFactory<>();
+ factory.setConsumerFactory(consumerFactory());
+ return factory;
+ }
+
+ @Bean
+ public DefaultKafkaConsumerFactory consumerFactory() {
+ Map props = new HashMap<>();
+ props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
+ props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
+ props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
+ props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
+ props.put(JsonDeserializer.TRUSTED_PACKAGES, "com.baeldung.spring.kafka.start.stop.consumer");
+ return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(),
+ new JsonDeserializer<>(UserEvent.class));
+ }
+}
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/KafkaListenerControlService.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/KafkaListenerControlService.java
new file mode 100644
index 0000000000..6d4c101c38
--- /dev/null
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/KafkaListenerControlService.java
@@ -0,0 +1,27 @@
+package com.baeldung.spring.kafka.startstopconsumer;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.config.KafkaListenerEndpointRegistry;
+import org.springframework.kafka.listener.MessageListenerContainer;
+import org.springframework.stereotype.Service;
+
+@Service
+public class KafkaListenerControlService {
+
+ @Autowired
+ private KafkaListenerEndpointRegistry registry;
+
+ public void startListener(String listenerId) {
+ MessageListenerContainer listenerContainer = registry.getListenerContainer(listenerId);
+ if (listenerContainer != null && !listenerContainer.isRunning()) {
+ listenerContainer.start();
+ }
+ }
+
+ public void stopListener(String listenerId) {
+ MessageListenerContainer listenerContainer = registry.getListenerContainer(listenerId);
+ if (listenerContainer != null && listenerContainer.isRunning()) {
+ listenerContainer.stop();
+ }
+ }
+}
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/StartStopConsumerApplication.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/StartStopConsumerApplication.java
new file mode 100644
index 0000000000..f0cfe88717
--- /dev/null
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/StartStopConsumerApplication.java
@@ -0,0 +1,11 @@
+package com.baeldung.spring.kafka.startstopconsumer;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class StartStopConsumerApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(StartStopConsumerApplication.class, args);
+ }
+}
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEvent.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEvent.java
new file mode 100644
index 0000000000..1c4692b8ee
--- /dev/null
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEvent.java
@@ -0,0 +1,20 @@
+package com.baeldung.spring.kafka.startstopconsumer;
+
+public class UserEvent {
+ private String userEventId;
+
+ public UserEvent() {
+ }
+
+ public UserEvent(String userEventId) {
+ this.userEventId = userEventId;
+ }
+
+ public String getUserEventId() {
+ return userEventId;
+ }
+
+ public void setUserEventId(String userEventId) {
+ this.userEventId = userEventId;
+ }
+}
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEventListener.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEventListener.java
new file mode 100644
index 0000000000..d68e70160f
--- /dev/null
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEventListener.java
@@ -0,0 +1,23 @@
+package com.baeldung.spring.kafka.startstopconsumer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.stereotype.Component;
+
+@Component
+public class UserEventListener {
+
+ private static final Logger logger = LoggerFactory.getLogger(UserEventListener.class);
+
+ @Autowired
+ UserEventStore userEventStore;
+
+ @KafkaListener(id = Constants.LISTENER_ID, topics = Constants.MULTI_PARTITION_TOPIC, groupId = "test-group",
+ containerFactory = "kafkaListenerContainerFactory", autoStartup = "false")
+ public void processUserEvent(UserEvent userEvent) {
+ logger.info("Received UserEvent: " + userEvent.getUserEventId());
+ userEventStore.addUserEvent(userEvent);
+ }
+}
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEventStore.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEventStore.java
new file mode 100644
index 0000000000..3da6f7352d
--- /dev/null
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/startstopconsumer/UserEventStore.java
@@ -0,0 +1,24 @@
+package com.baeldung.spring.kafka.startstopconsumer;
+
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class UserEventStore {
+
+ private final List userEvents = new ArrayList<>();
+
+ public void addUserEvent(UserEvent userEvent) {
+ userEvents.add(userEvent);
+ }
+
+ public List getUserEvents() {
+ return userEvents;
+ }
+
+ public void clearUserEvents() {
+ this.userEvents.clear();
+ }
+}
diff --git a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/viewheaders/KafkaMessageConsumer.java b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/viewheaders/KafkaMessageConsumer.java
index 3bdc13d968..a9a414ac9f 100644
--- a/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/viewheaders/KafkaMessageConsumer.java
+++ b/spring-kafka-3/src/main/java/com/baeldung/spring/kafka/viewheaders/KafkaMessageConsumer.java
@@ -20,13 +20,13 @@ public class KafkaMessageConsumer {
String topicName = (String) headers.get(KafkaHeaders.TOPIC);
System.out.println("Topic: " + topicName);
- int partitionID = (int) headers.get(KafkaHeaders.RECEIVED_PARTITION_ID);
+ int partitionID = (int) headers.get(KafkaHeaders.RECEIVED_PARTITION);
System.out.println("Partition ID: " + partitionID);
}
@KafkaListener(topics = { "my-topic" }, groupId = "my-consumer-group")
public void listen(@Payload String message, @Header(KafkaHeaders.RECEIVED_TOPIC) String topicName,
- @Header(KafkaHeaders.RECEIVED_PARTITION_ID) int partition) {
+ @Header(KafkaHeaders.RECEIVED_PARTITION) int partition) {
System.out.println("Topic: " + topicName);
System.out.println("Partition ID: " + partition);
}
diff --git a/spring-kafka-3/src/test/java/com/baeldung/spring/kafka/startstopconsumer/StartStopConsumerLiveTest.java b/spring-kafka-3/src/test/java/com/baeldung/spring/kafka/startstopconsumer/StartStopConsumerLiveTest.java
new file mode 100644
index 0000000000..7ea8729111
--- /dev/null
+++ b/spring-kafka-3/src/test/java/com/baeldung/spring/kafka/startstopconsumer/StartStopConsumerLiveTest.java
@@ -0,0 +1,105 @@
+package com.baeldung.spring.kafka.startstopconsumer;
+
+import org.apache.kafka.clients.producer.KafkaProducer;
+import org.apache.kafka.clients.producer.ProducerConfig;
+import org.apache.kafka.clients.producer.ProducerRecord;
+import org.apache.kafka.clients.producer.RecordMetadata;
+import org.apache.kafka.common.serialization.LongSerializer;
+import org.awaitility.Awaitility;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.kafka.support.serializer.JsonSerializer;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+import org.testcontainers.containers.KafkaContainer;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.DockerImageName;
+
+import static java.time.Duration.ofMillis;
+import static java.time.Duration.ofSeconds;
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+import java.util.*;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+// This live test needs a Docker Daemon running so that a kafka container can be created
+@Testcontainers
+@SpringBootTest(classes = StartStopConsumerApplication.class)
+public class StartStopConsumerLiveTest {
+
+ private static KafkaProducer producer;
+
+ private static final Logger logger = LoggerFactory.getLogger(StartStopConsumerLiveTest.class);
+
+ @Container
+ private static KafkaContainer KAFKA_CONTAINER = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest"));
+
+ @Autowired
+ KafkaListenerControlService kafkaListenerControlService;
+
+ @Autowired
+ UserEventStore userEventStore;
+
+ @DynamicPropertySource
+ static void setProps(DynamicPropertyRegistry registry) {
+ registry.add("spring.kafka.bootstrap-servers", KAFKA_CONTAINER::getBootstrapServers);
+ }
+
+ @BeforeAll
+ static void beforeAll() {
+ Properties producerProperties = new Properties();
+ producerProperties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_CONTAINER.getBootstrapServers());
+ producerProperties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, LongSerializer.class.getName());
+ producerProperties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class.getName());
+ producer = new KafkaProducer<>(producerProperties);
+ Awaitility.setDefaultTimeout(ofSeconds(5));
+ Awaitility.setDefaultPollInterval(ofMillis(50));
+ }
+
+ @AfterAll
+ static void destroy() {
+ KAFKA_CONTAINER.stop();
+ }
+
+ @BeforeEach
+ void beforeEach() {
+ this.userEventStore.clearUserEvents();
+ }
+
+ @Test
+ void processMessages_whenListenerIsRestarted_thenCorrectNumberOfMessagesAreConsumed() throws ExecutionException, InterruptedException {
+ kafkaListenerControlService.startListener(Constants.LISTENER_ID);
+
+ //Verification that listener has started.
+ UserEvent startUserEventTest = new UserEvent(UUID.randomUUID().toString());
+ producer.send(new ProducerRecord<>(Constants.MULTI_PARTITION_TOPIC, startUserEventTest));
+ await().untilAsserted(() -> assertEquals(1, this.userEventStore.getUserEvents().size()));
+ this.userEventStore.clearUserEvents();
+
+ for (long count = 1; count <= 10; count++) {
+ UserEvent userEvent = new UserEvent(UUID.randomUUID().toString());
+ Future future = producer.send(new ProducerRecord<>(Constants.MULTI_PARTITION_TOPIC, userEvent));
+ RecordMetadata metadata = future.get();
+ if (count == 4) {
+ await().untilAsserted(() -> assertEquals(4, this.userEventStore.getUserEvents().size()));
+ this.kafkaListenerControlService.stopListener(Constants.LISTENER_ID);
+ this.userEventStore.clearUserEvents();
+ }
+ logger.info("User Event ID: " + userEvent.getUserEventId() + ", Partition : " + metadata.partition());
+ }
+ assertEquals(0, this.userEventStore.getUserEvents().size());
+ kafkaListenerControlService.startListener(Constants.LISTENER_ID);
+ await().untilAsserted(() -> assertEquals(6, this.userEventStore.getUserEvents().size()));
+ kafkaListenerControlService.stopListener(Constants.LISTENER_ID);
+ }
+}
\ No newline at end of file
diff --git a/spring-kafka/README.md b/spring-kafka/README.md
index 6e495b1210..ed6c3bf8d7 100644
--- a/spring-kafka/README.md
+++ b/spring-kafka/README.md
@@ -8,7 +8,6 @@ This module contains articles about Spring with Kafka
- [Testing Kafka and Spring Boot](https://www.baeldung.com/spring-boot-kafka-testing)
- [Monitor the Consumer Lag in Apache Kafka](https://www.baeldung.com/java-kafka-consumer-lag)
- [Send Large Messages With Kafka](https://www.baeldung.com/java-kafka-send-large-message)
-- [Configuring Kafka SSL Using Spring Boot](https://www.baeldung.com/spring-boot-kafka-ssl)
- [Kafka Streams With Spring Boot](https://www.baeldung.com/spring-boot-kafka-streams)
- [Get the Number of Messages in an Apache Kafka Topic](https://www.baeldung.com/java-kafka-count-topic-messages)
- [Sending Data to a Specific Partition in Kafka](https://www.baeldung.com/kafka-send-data-partition)
diff --git a/spring-reactive-modules/pom.xml b/spring-reactive-modules/pom.xml
index 6ab85c88b4..a8a9d6de8a 100644
--- a/spring-reactive-modules/pom.xml
+++ b/spring-reactive-modules/pom.xml
@@ -24,7 +24,7 @@
spring-reactive-client-2
spring-reactive-filters
spring-reactive-oauth
- spring-reactive-security
+
spring-reactive-data-couchbase
spring-reactive
spring-reactive-exceptions
diff --git a/spring-reactive-modules/spring-reactive-data/pom.xml b/spring-reactive-modules/spring-reactive-data/pom.xml
index d72072e419..80f7a76ff0 100644
--- a/spring-reactive-modules/spring-reactive-data/pom.xml
+++ b/spring-reactive-modules/spring-reactive-data/pom.xml
@@ -8,9 +8,10 @@
jar
- com.baeldung.spring.reactive
- spring-reactive-modules
- 1.0.0-SNAPSHOT
+ com.baeldung
+ parent-boot-3
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-3
@@ -41,11 +42,6 @@
h2
runtime
-
- io.r2dbc
- r2dbc-h2
- runtime
-
org.projectlombok
lombok
@@ -58,9 +54,9 @@
test
- javax.validation
- validation-api
- ${validation-api.version}
+ jakarta.validation
+ jakarta.validation-api
+ ${jakarta.validation-api.version}
io.r2dbc
@@ -69,8 +65,8 @@
- UTF-8
- 2.0.1.Final
+ 3.1.0-M1
+ true
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-data/src/main/java/com/baeldung/pagination/model/Product.java b/spring-reactive-modules/spring-reactive-data/src/main/java/com/baeldung/pagination/model/Product.java
index c82e31309c..b97aefaaee 100644
--- a/spring-reactive-modules/spring-reactive-data/src/main/java/com/baeldung/pagination/model/Product.java
+++ b/spring-reactive-modules/spring-reactive-data/src/main/java/com/baeldung/pagination/model/Product.java
@@ -2,8 +2,8 @@ package com.baeldung.pagination.model;
import java.util.UUID;
-import javax.validation.constraints.NotNull;
-import javax.validation.constraints.Size;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
diff --git a/spring-reactive-modules/spring-reactive-data/src/main/java/com/baeldung/pagination/repository/ProductRepository.java b/spring-reactive-modules/spring-reactive-data/src/main/java/com/baeldung/pagination/repository/ProductRepository.java
index 1610d452da..f2ddc63ecb 100644
--- a/spring-reactive-modules/spring-reactive-data/src/main/java/com/baeldung/pagination/repository/ProductRepository.java
+++ b/spring-reactive-modules/spring-reactive-data/src/main/java/com/baeldung/pagination/repository/ProductRepository.java
@@ -9,8 +9,11 @@ import org.springframework.stereotype.Repository;
import com.baeldung.pagination.model.Product;
import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
@Repository
public interface ProductRepository extends ReactiveSortingRepository {
Flux findAllBy(Pageable pageable);
+
+ Mono count();
}
diff --git a/spring-reactive-modules/spring-reactive-security/pom.xml b/spring-reactive-modules/spring-reactive-security/pom.xml
index d501a03c46..ea886f5855 100644
--- a/spring-reactive-modules/spring-reactive-security/pom.xml
+++ b/spring-reactive-modules/spring-reactive-security/pom.xml
@@ -10,9 +10,10 @@
spring boot security sample project about new features
- com.baeldung.spring.reactive
- spring-reactive-modules
- 1.0.0-SNAPSHOT
+ com.baeldung
+ parent-boot-3
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-3
@@ -34,8 +35,8 @@
${reactor-spring.version}
- javax.json.bind
- javax.json.bind-api
+ jakarta.json.bind
+ jakarta.json.bind-api
org.projectlombok
@@ -51,6 +52,7 @@
org.apache.johnzon
johnzon-jsonb
+ ${johnzon-jsonb.version}
@@ -63,6 +65,11 @@
spring-boot-devtools
runtime
+
+ jakarta.json
+ jakarta.json-api
+ ${jakarta.json-api.version}
+
org.springframework.boot
spring-boot-starter-test
@@ -117,6 +124,8 @@
1.0
3.1.6.RELEASE
3.4.29
+ 2.0.1
+ 2.0.0
\ No newline at end of file
diff --git a/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/authresolver/CustomWebSecurityConfig.java b/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/authresolver/CustomWebSecurityConfig.java
index dc5eab3dd5..77f83be28d 100644
--- a/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/authresolver/CustomWebSecurityConfig.java
+++ b/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/authresolver/CustomWebSecurityConfig.java
@@ -2,10 +2,13 @@ package com.baeldung.reactive.authresolver;
import java.util.Collections;
import org.springframework.context.annotation.Bean;
+import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
@@ -24,12 +27,10 @@ public class CustomWebSecurityConfig {
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
- .authorizeExchange()
- .pathMatchers("/**")
- .authenticated()
- .and()
- .httpBasic()
- .disable()
+ .csrf(csrfSpec -> csrfSpec.disable())
+ .authorizeExchange(auth -> auth.pathMatchers(HttpMethod.GET,"/**")
+ .authenticated())
+ .httpBasic(httpBasicSpec -> httpBasicSpec.disable())
.addFilterAfter(authenticationWebFilter(), SecurityWebFiltersOrder.REACTOR_CONTEXT)
.build();
}
diff --git a/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/cors/global/CorsGlobalConfigApplication.java b/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/cors/global/CorsGlobalConfigApplication.java
index a70f937980..8be6484e68 100644
--- a/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/cors/global/CorsGlobalConfigApplication.java
+++ b/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/cors/global/CorsGlobalConfigApplication.java
@@ -27,7 +27,7 @@ public class CorsGlobalConfigApplication {
@Bean
public SecurityWebFilterChain corsGlobalSpringSecurityFilterChain(ServerHttpSecurity http) {
- http.csrf().disable();
+ http.csrf(csrfSpec -> csrfSpec.disable());
return http.build();
}
}
diff --git a/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/cors/webfilter/CorsWebFilterApplication.java b/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/cors/webfilter/CorsWebFilterApplication.java
index 7792975768..343151498a 100644
--- a/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/cors/webfilter/CorsWebFilterApplication.java
+++ b/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/reactive/cors/webfilter/CorsWebFilterApplication.java
@@ -27,7 +27,7 @@ public class CorsWebFilterApplication {
@Bean
public SecurityWebFilterChain corsWebfilterSpringSecurityFilterChain(ServerHttpSecurity http) {
- http.csrf().disable();
+ http.csrf(csrfSpec -> csrfSpec.disable());
return http.build();
}
diff --git a/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/webflux/EmployeeWebSecurityConfig.java b/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/webflux/EmployeeWebSecurityConfig.java
index 75475a0f08..929e466169 100644
--- a/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/webflux/EmployeeWebSecurityConfig.java
+++ b/spring-reactive-modules/spring-reactive-security/src/main/java/com/baeldung/webflux/EmployeeWebSecurityConfig.java
@@ -3,6 +3,7 @@ package com.baeldung.webflux;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
@@ -27,15 +28,13 @@ public class EmployeeWebSecurityConfig {
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
- http.csrf()
- .disable()
- .authorizeExchange()
+ http.csrf(csrfSpec -> csrfSpec.disable())
+ .authorizeExchange(auth -> auth
.pathMatchers(HttpMethod.POST, "/employees/update")
.hasRole("ADMIN")
.pathMatchers("/**")
- .permitAll()
- .and()
- .httpBasic();
+ .permitAll())
+ .httpBasic(Customizer.withDefaults());
return http.build();
}
diff --git a/spring-reactive-modules/spring-reactive-security/src/test/java/com/baeldung/reactive/authresolver/AuthResolverIntegrationTest.java b/spring-reactive-modules/spring-reactive-security/src/test/java/com/baeldung/reactive/authresolver/AuthResolverIntegrationTest.java
index 9e0855d086..ca3ea19e09 100644
--- a/spring-reactive-modules/spring-reactive-security/src/test/java/com/baeldung/reactive/authresolver/AuthResolverIntegrationTest.java
+++ b/spring-reactive-modules/spring-reactive-security/src/test/java/com/baeldung/reactive/authresolver/AuthResolverIntegrationTest.java
@@ -1,19 +1,21 @@
package com.baeldung.reactive.authresolver;
import java.util.Base64;
-
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
@RunWith(SpringRunner.class)
-@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = AuthResolverApplication.class)
+@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = {AuthResolverApplication.class, AuthResolverController.class, CustomWebSecurityConfig.class})
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@AutoConfigureWebTestClient(timeout = "36000000")
public class AuthResolverIntegrationTest {
@Autowired
private WebTestClient testClient;
diff --git a/spring-reactive-modules/spring-webflux-amqp/pom.xml b/spring-reactive-modules/spring-webflux-amqp/pom.xml
index 8ab8277d08..67faed216e 100755
--- a/spring-reactive-modules/spring-webflux-amqp/pom.xml
+++ b/spring-reactive-modules/spring-webflux-amqp/pom.xml
@@ -11,24 +11,12 @@
Spring WebFlux AMQP Sample
- com.baeldung.spring.reactive
- spring-reactive-modules
- 1.0.0-SNAPSHOT
+ com.baeldung
+ parent-boot-3
+ 0.0.1-SNAPSHOT
+ ../../parent-boot-3
-
-
-
-
- org.springframework.boot
- spring-boot-dependencies
-
- 2.1.3.RELEASE
- pom
- import
-
-
-
diff --git a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/AmqpReactiveController.java b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/AmqpReactiveController.java
index b71c32bd05..009ec87ddd 100644
--- a/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/AmqpReactiveController.java
+++ b/spring-reactive-modules/spring-webflux-amqp/src/main/java/com/baeldung/spring/amqp/AmqpReactiveController.java
@@ -2,7 +2,7 @@ package com.baeldung.spring.amqp;
import java.time.Duration;
-import javax.annotation.PostConstruct;
+import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,7 +31,7 @@ import reactor.core.publisher.Mono;
@RestController
public class AmqpReactiveController {
- private static Logger log = LoggerFactory.getLogger(AmqpReactiveController.class);
+ private static final Logger log = LoggerFactory.getLogger(AmqpReactiveController.class);
@Autowired
private AmqpTemplate amqpTemplate;
@@ -142,10 +142,10 @@ public class AmqpReactiveController {
MessageListenerContainer mlc = messageListenerContainerFactory.createMessageListenerContainer(d.getRoutingKey());
- Flux f = Flux. create(emitter -> {
+ Flux f = Flux.create(emitter -> {
log.info("[I168] Adding listener, queue={}", d.getRoutingKey());
- mlc.setupMessageListener((MessageListener) m -> {
+ mlc.setupMessageListener(m -> {
String qname = m.getMessageProperties()
.getConsumerQueue();
@@ -233,11 +233,11 @@ public class AmqpReactiveController {
MessageListenerContainer mlc = messageListenerContainerFactory.createMessageListenerContainer(qname);
- Flux f = Flux. create(emitter -> {
+ Flux f = Flux.create(emitter -> {
log.info("[I168] Adding listener, queue={}", qname);
- mlc.setupMessageListener((MessageListener) m -> {
+ mlc.setupMessageListener(m -> {
log.info("[I137] Message received, queue={}", qname);
diff --git a/spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/amqp/SpringWebfluxAmqpLiveTest.java b/spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/amqp/SpringWebfluxAmqpLiveTest.java
index 81782ce575..7829a9db67 100644
--- a/spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/amqp/SpringWebfluxAmqpLiveTest.java
+++ b/spring-reactive-modules/spring-webflux-amqp/src/test/java/com/baeldung/spring/amqp/SpringWebfluxAmqpLiveTest.java
@@ -15,7 +15,7 @@ public class SpringWebfluxAmqpLiveTest {
client.post()
.uri("/queue/NYSE")
- .syncBody("Test Message")
+ .bodyValue("Test Message")
.exchange()
.expectStatus().isAccepted();
diff --git a/spring-security-modules/spring-security-core-2/pom.xml b/spring-security-modules/spring-security-core-2/pom.xml
index ace629eef1..94940f2613 100644
--- a/spring-security-modules/spring-security-core-2/pom.xml
+++ b/spring-security-modules/spring-security-core-2/pom.xml
@@ -17,6 +17,7 @@
com.baeldung.authresolver.AuthResolverApplication
+ 0.12.5
@@ -61,6 +62,21 @@
org.springframework.security
spring-security-core
+
+ io.jsonwebtoken
+ jjwt-api
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${jjwt.version}
+
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/SpringJwtApplication.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/SpringJwtApplication.java
new file mode 100644
index 0000000000..611935e566
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/SpringJwtApplication.java
@@ -0,0 +1,14 @@
+package com.baeldung.jwtsignkey;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+
+@SpringBootApplication
+@EnableWebMvc
+public class SpringJwtApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(com.baeldung.jwtsignkey.SpringJwtApplication.class);
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/controller/JwtAuthController.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/controller/JwtAuthController.java
new file mode 100644
index 0000000000..37de1682db
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/controller/JwtAuthController.java
@@ -0,0 +1,77 @@
+package com.baeldung.jwtsignkey.controller;
+
+import com.baeldung.jwtsignkey.jwtconfig.JwtUtils;
+import com.baeldung.jwtsignkey.model.User;
+import com.baeldung.jwtsignkey.repository.UserRepository;
+import com.baeldung.jwtsignkey.response.JwtResponse;
+import com.baeldung.jwtsignkey.userservice.UserDetailsImpl;
+import com.baeldung.request.LoginRequest;
+import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.UnsupportedEncodingException;
+
+@RestController
+public class JwtAuthController {
+
+ @Autowired
+ AuthenticationManager authenticationManager;
+
+ @Autowired
+ PasswordEncoder encoder;
+
+ @Autowired
+ JwtUtils jwtUtils;
+
+ @Autowired
+ UserRepository userRepository;
+
+ @PostMapping("/signup")
+ public ResponseEntity> registerUser(@RequestBody User signUpRequest, HttpServletRequest request) throws UnsupportedEncodingException {
+
+ if (userRepository.existsByUsername(signUpRequest.getUsername())) {
+ return ResponseEntity.badRequest()
+ .body("Error: Username is already taken!");
+ }
+ User user = new User();
+ user.setUsername(signUpRequest.getUsername());
+ user.setPassword(encoder.encode(signUpRequest.getPassword()));
+
+ userRepository.save(user);
+
+ return ResponseEntity.ok(user);
+ }
+
+ @PostMapping("/signin")
+ public ResponseEntity> authenticateUser(@RequestBody LoginRequest loginRequest) {
+
+ Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
+
+ SecurityContextHolder.getContext()
+ .setAuthentication(authentication);
+ UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
+
+ String jwt = jwtUtils.generateJwtToken(authentication);
+
+ return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getUsername()));
+
+ }
+
+ @RequestMapping("/user-dashboard")
+ @PreAuthorize("isAuthenticated()")
+ public String dashboard() {
+ return "My Dashboard";
+ }
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthEntryPointJwt.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthEntryPointJwt.java
new file mode 100644
index 0000000000..1cc64f3550
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthEntryPointJwt.java
@@ -0,0 +1,39 @@
+package com.baeldung.jwtsignkey.jwtconfig;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class AuthEntryPointJwt implements AuthenticationEntryPoint {
+
+ private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
+
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
+ logger.error("Unauthorized error: {}", authException.getMessage());
+
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+
+ final Map body = new HashMap<>();
+ body.put("status", HttpServletResponse.SC_UNAUTHORIZED);
+ body.put("error", "Unauthorized");
+ body.put("message", authException.getMessage());
+ body.put("path", request.getServletPath());
+
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(response.getOutputStream(), body);
+ }
+}
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthTokenFilter.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthTokenFilter.java
new file mode 100644
index 0000000000..a149c71121
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/AuthTokenFilter.java
@@ -0,0 +1,59 @@
+package com.baeldung.jwtsignkey.jwtconfig;
+
+import com.baeldung.jwtsignkey.userservice.UserDetailsServiceImpl;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+public class AuthTokenFilter extends OncePerRequestFilter {
+ @Autowired
+ private JwtUtils jwtUtils;
+
+ @Autowired
+ private UserDetailsServiceImpl userDetailsService;
+
+ private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ try {
+ String jwt = parseJwt(request);
+ if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
+ String username = jwtUtils.getUserNameFromJwtToken(jwt);
+
+ UserDetails userDetails = userDetailsService.loadUserByUsername(username);
+ UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+ authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+
+ SecurityContextHolder.getContext()
+ .setAuthentication(authentication);
+ }
+ } catch (Exception e) {
+ logger.error("Cannot set user authentication: {}", e);
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ private String parseJwt(HttpServletRequest request) {
+ String headerAuth = request.getHeader("Authorization");
+
+ if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
+ return headerAuth.substring(7, headerAuth.length());
+ }
+
+ return null;
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/JwtUtils.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/JwtUtils.java
new file mode 100644
index 0000000000..5fbd4c6de3
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/jwtconfig/JwtUtils.java
@@ -0,0 +1,81 @@
+package com.baeldung.jwtsignkey.jwtconfig;
+
+import com.baeldung.jwtsignkey.userservice.UserDetailsImpl;
+
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.MalformedJwtException;
+import io.jsonwebtoken.UnsupportedJwtException;
+import io.jsonwebtoken.io.Decoders;
+import io.jsonwebtoken.security.Keys;
+import io.jsonwebtoken.security.SignatureException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import java.util.Date;
+
+@Component
+public class JwtUtils {
+ private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
+
+ @Value("${baeldung.app.jwtSecret}")
+ private String jwtSecret;
+
+ @Value("${baeldung.app.jwtExpirationMs}")
+ private int jwtExpirationMs;
+
+ public String generateJwtToken(Authentication authentication) {
+
+ UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
+
+ return Jwts.builder()
+ .subject((userPrincipal.getUsername()))
+ .issuedAt(new Date())
+ .expiration(new Date((new Date()).getTime() + jwtExpirationMs))
+ .signWith(getSigningKey())
+ .compact();
+
+ }
+
+ private SecretKey getSigningKey() {
+ byte[] keyBytes = Decoders.BASE64.decode(jwtSecret);
+ return Keys.hmacShaKeyFor(keyBytes);
+ }
+
+ public String getUserNameFromJwtToken(String token) {
+ return Jwts.parser()
+ .verifyWith(getSigningKey())
+ .build()
+ .parseSignedClaims(token)
+ .getPayload()
+ .getSubject();
+
+ }
+
+ public boolean validateJwtToken(String authToken) {
+ try {
+ Jwts.parser()
+ .verifyWith(getSigningKey())
+ .build()
+ .parseSignedClaims(authToken);
+ return true;
+ } catch (SignatureException e) {
+ logger.error("Invalid JWT signature: {}", e.getMessage());
+ } catch (MalformedJwtException e) {
+ logger.error("Invalid JWT token: {}", e.getMessage());
+ } catch (ExpiredJwtException e) {
+ logger.error("JWT token is expired: {}", e.getMessage());
+ } catch (UnsupportedJwtException e) {
+ logger.error("JWT token is unsupported: {}", e.getMessage());
+ } catch (IllegalArgumentException e) {
+ logger.error("JWT claims string is empty: {}", e.getMessage());
+ }
+
+ return false;
+ }
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/model/User.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/model/User.java
new file mode 100644
index 0000000000..c7acda047a
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/model/User.java
@@ -0,0 +1,45 @@
+package com.baeldung.jwtsignkey.model;
+
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+
+@Entity
+public class User {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ private String username;
+
+ private String password;
+
+ public User() {
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/repository/UserRepository.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/repository/UserRepository.java
new file mode 100644
index 0000000000..886338242c
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/repository/UserRepository.java
@@ -0,0 +1,14 @@
+package com.baeldung.jwtsignkey.repository;
+
+import com.baeldung.jwtsignkey.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface UserRepository extends JpaRepository {
+
+ Boolean existsByUsername(String username);
+
+ Optional findByUsername(String username);
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/response/JwtResponse.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/response/JwtResponse.java
new file mode 100644
index 0000000000..f5b3148c42
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/response/JwtResponse.java
@@ -0,0 +1,37 @@
+package com.baeldung.jwtsignkey.response;
+
+public class JwtResponse {
+ private String token;
+ private String type = "Bearer";
+
+ private String username;
+
+ public JwtResponse(String accessToken, String username) {
+ this.token = accessToken;
+ this.username = username;
+ }
+
+ public String getAccessToken() {
+ return token;
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.token = accessToken;
+ }
+
+ public String getTokenType() {
+ return type;
+ }
+
+ public void setTokenType(String tokenType) {
+ this.type = tokenType;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/securityconfig/SecurityConfiguration.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/securityconfig/SecurityConfiguration.java
new file mode 100644
index 0000000000..30e8c552c9
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/securityconfig/SecurityConfiguration.java
@@ -0,0 +1,79 @@
+package com.baeldung.jwtsignkey.securityconfig;
+
+import com.baeldung.jwtsignkey.jwtconfig.AuthEntryPointJwt;
+import com.baeldung.jwtsignkey.jwtconfig.AuthTokenFilter;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+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.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
+import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
+
+import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;
+
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity
+public class SecurityConfiguration {
+
+ @Autowired
+ UserDetailsService userDetailsService;
+ @Autowired
+ private AuthEntryPointJwt unauthorizedHandler;
+
+ private static final String[] WHITE_LIST_URL = { "/h2-console/**", "/signin", "/signup", "/user-dashboard" };
+
+ @Bean
+ public AuthTokenFilter authenticationJwtTokenFilter() {
+ return new AuthTokenFilter();
+ }
+
+ @Bean
+ public DaoAuthenticationProvider authenticationProvider() {
+ DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
+
+ authProvider.setUserDetailsService(userDetailsService);
+ authProvider.setPasswordEncoder(passwordEncoder());
+
+ return authProvider;
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
+ return authConfig.getAuthenticationManager();
+ }
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception {
+ http.csrf(AbstractHttpConfigurer::disable)
+ .cors(AbstractHttpConfigurer::disable)
+ .authorizeHttpRequests(req -> req.requestMatchers(WHITE_LIST_URL)
+ .permitAll()
+ .anyRequest()
+ .authenticated())
+ .exceptionHandling(ex -> ex.authenticationEntryPoint(unauthorizedHandler))
+ .sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
+ .authenticationProvider(authenticationProvider())
+ .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsImpl.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsImpl.java
new file mode 100644
index 0000000000..a651a1088a
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsImpl.java
@@ -0,0 +1,85 @@
+package com.baeldung.jwtsignkey.userservice;
+
+import com.baeldung.jwtsignkey.model.User;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.Objects;
+
+public class UserDetailsImpl implements UserDetails {
+ private static final long serialVersionUID = 1L;
+
+ private Long id;
+
+ private String username;
+
+ @JsonIgnore
+ private String password;
+
+ public UserDetailsImpl(Long id, String username, String password) {
+ this.id = id;
+ this.username = username;
+ this.password = password;
+
+ }
+
+ public static UserDetailsImpl build(User user) {
+
+ return new UserDetailsImpl(user.getId(), user.getUsername(), user.getPassword());
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public static long getSerialversionuid() {
+ return serialVersionUID;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return null;
+ }
+
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String getUsername() {
+ return username;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ UserDetailsImpl user = (UserDetailsImpl) o;
+ return Objects.equals(id, user.id);
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsServiceImpl.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsServiceImpl.java
new file mode 100644
index 0000000000..c544f748cd
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/jwtsignkey/userservice/UserDetailsServiceImpl.java
@@ -0,0 +1,28 @@
+package com.baeldung.jwtsignkey.userservice;
+
+import com.baeldung.jwtsignkey.model.User;
+import com.baeldung.jwtsignkey.repository.UserRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+;
+
+@Service
+public class UserDetailsServiceImpl implements UserDetailsService {
+
+ @Autowired
+ UserRepository userRepository;
+
+ @Override
+ @Transactional
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ User user = userRepository.findByUsername(username)
+ .orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
+
+ return UserDetailsImpl.build(user);
+ }
+}
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/request/LoginRequest.java b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/request/LoginRequest.java
new file mode 100644
index 0000000000..24555136c2
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/main/java/com/baeldung/request/LoginRequest.java
@@ -0,0 +1,24 @@
+package com.baeldung.request;
+
+public class LoginRequest {
+
+ private String username;
+
+ private String password;
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/spring-security-modules/spring-security-core-2/src/main/resources/application.properties b/spring-security-modules/spring-security-core-2/src/main/resources/application.properties
index 9d154c9cc0..6005cdd2d2 100644
--- a/spring-security-modules/spring-security-core-2/src/main/resources/application.properties
+++ b/spring-security-modules/spring-security-core-2/src/main/resources/application.properties
@@ -1 +1,2 @@
-spring.thymeleaf.prefix=classpath:/templates/
\ No newline at end of file
+baeldung.app.jwtSecret= 404E635266556A586E3272357538782F413F4428472B4B6250645367566B5970
+baeldung.app.jwtExpirationMs= 8640000
diff --git a/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/jwtsecuritykey/JwtSignKeyIntegrationTest.java b/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/jwtsecuritykey/JwtSignKeyIntegrationTest.java
new file mode 100644
index 0000000000..73d1142ceb
--- /dev/null
+++ b/spring-security-modules/spring-security-core-2/src/test/java/com/baeldung/jwtsecuritykey/JwtSignKeyIntegrationTest.java
@@ -0,0 +1,40 @@
+package com.baeldung.jwtsecuritykey;
+
+import com.baeldung.jwtsignkey.SpringJwtApplication;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@SpringBootTest(classes = SpringJwtApplication.class)
+public class JwtSignKeyIntegrationTest {
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mvc;
+
+ @BeforeEach
+ private void setup() {
+ mvc = MockMvcBuilders.webAppContextSetup(context)
+ .apply(springSecurity())
+ .build();
+ }
+
+
+ @Test
+ public void givenAnonymous_whenAccessUserDashboard_thenForbidden() throws Exception {
+ mvc.perform(get("/user-dashboard"))
+ .andExpect(status().isUnauthorized());
+ }
+
+
+}
+
diff --git a/spring-security-modules/spring-security-pkce/pkce-auth-server/pom.xml b/spring-security-modules/spring-security-pkce/pkce-auth-server/pom.xml
index 1fba6501f0..f1a8c71077 100644
--- a/spring-security-modules/spring-security-pkce/pkce-auth-server/pom.xml
+++ b/spring-security-modules/spring-security-pkce/pkce-auth-server/pom.xml
@@ -18,12 +18,7 @@
org.springframework.security
spring-security-oauth2-authorization-server
- ${spring-authorization-server.version}
-
- 0.3.1
-
-
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-pkce/pkce-auth-server/src/main/java/com/baeldung/security/pkce/authserver/conf/AuthServerConfiguration.java b/spring-security-modules/spring-security-pkce/pkce-auth-server/src/main/java/com/baeldung/security/pkce/authserver/conf/AuthServerConfiguration.java
index b599880f3c..326bfc5bda 100644
--- a/spring-security-modules/spring-security-pkce/pkce-auth-server/src/main/java/com/baeldung/security/pkce/authserver/conf/AuthServerConfiguration.java
+++ b/spring-security-modules/spring-security-pkce/pkce-auth-server/src/main/java/com/baeldung/security/pkce/authserver/conf/AuthServerConfiguration.java
@@ -5,24 +5,22 @@ import java.util.UUID;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
+import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
-import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
-import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
-import org.springframework.security.crypto.password.NoOpPasswordEncoder;
-import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
-import org.springframework.security.oauth2.server.authorization.config.ClientSettings;
-import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
+import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
+import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
+import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
+import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
-import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
@Configuration
public class AuthServerConfiguration {
@@ -30,38 +28,30 @@ public class AuthServerConfiguration {
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
-
- OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer<>();
- // @formatter:off
+ OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
+ http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
+ .oidc(Customizer.withDefaults());
http
- .requestMatcher(authorizationServerConfigurer.getEndpointsMatcher())
- .authorizeRequests(authorize ->
- authorize
- .anyRequest()
- .authenticated());
- http
- .exceptionHandling(exceptions ->
- exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
- .csrf( csrf ->
- csrf
- .ignoringRequestMatchers(authorizationServerConfigurer.getEndpointsMatcher()))
- .apply(authorizationServerConfigurer);
-
- // Required by /userinfo endpoint
- http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
+ // Redirect to the login page when not authenticated from the
+ // authorization endpoint
+ .exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"),
+ new MediaTypeRequestMatcher(MediaType.TEXT_HTML)))
+ // Accept access tokens for User Info and/or Client Registration
+ .oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));
+
return http.build();
- // @formatter:on
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
- // @formatter:off
- return http
- .formLogin(Customizer.withDefaults())
- .build();
- // @formatter:on
+ http
+ .authorizeHttpRequests((authorize) -> authorize
+ .anyRequest().authenticated()
+ )
+ .formLogin(Customizer.withDefaults());
+ return http.build();
}
@Bean
@@ -89,8 +79,8 @@ public class AuthServerConfiguration {
}
@Bean
- public ProviderSettings providerSettings() {
- return ProviderSettings
+ public AuthorizationServerSettings authorizationServerSettings() {
+ return AuthorizationServerSettings
.builder()
.build();
}
diff --git a/spring-security-modules/spring-security-pkce/pkce-auth-server/src/main/java/com/baeldung/security/pkce/authserver/conf/JwksConfiguration.java b/spring-security-modules/spring-security-pkce/pkce-auth-server/src/main/java/com/baeldung/security/pkce/authserver/conf/JwksConfiguration.java
index a253141101..e3307a7a30 100644
--- a/spring-security-modules/spring-security-pkce/pkce-auth-server/src/main/java/com/baeldung/security/pkce/authserver/conf/JwksConfiguration.java
+++ b/spring-security-modules/spring-security-pkce/pkce-auth-server/src/main/java/com/baeldung/security/pkce/authserver/conf/JwksConfiguration.java
@@ -8,8 +8,8 @@ import java.util.UUID;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
diff --git a/spring-security-modules/spring-security-pkce/pom.xml b/spring-security-modules/spring-security-pkce/pom.xml
index e0bd8eb90e..7c86e2852a 100644
--- a/spring-security-modules/spring-security-pkce/pom.xml
+++ b/spring-security-modules/spring-security-pkce/pom.xml
@@ -10,7 +10,8 @@
com.baeldung
- spring-security-modules
+ parent-boot-3
+ ../../parent-boot-3
0.0.1-SNAPSHOT
diff --git a/spring-security-modules/spring-security-web-boot-2/src/main/java/com/baeldung/loginredirect/LoginRedirectApplication.java b/spring-security-modules/spring-security-web-boot-2/src/main/java/com/baeldung/loginredirect/LoginRedirectApplication.java
index 1e44240449..e9d2cc08e8 100644
--- a/spring-security-modules/spring-security-web-boot-2/src/main/java/com/baeldung/loginredirect/LoginRedirectApplication.java
+++ b/spring-security-modules/spring-security-web-boot-2/src/main/java/com/baeldung/loginredirect/LoginRedirectApplication.java
@@ -5,6 +5,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;
@SpringBootApplication
+//Comment this line when you want to use the class definition for defining Spring security rules, LoginRedirectSecurityConfig.
+// Uncomment the annotations from LoginRedirectSecurityConfig.
@ImportResource({"classpath*:spring-security-login-redirect.xml"})
class LoginRedirectApplication {
public static void main(String[] args) {
diff --git a/spring-security-modules/spring-security-web-boot-2/src/main/java/com/baeldung/loginredirect/LoginRedirectSecurityConfig.java b/spring-security-modules/spring-security-web-boot-2/src/main/java/com/baeldung/loginredirect/LoginRedirectSecurityConfig.java
index c9aa607a9f..c19c2ce03e 100644
--- a/spring-security-modules/spring-security-web-boot-2/src/main/java/com/baeldung/loginredirect/LoginRedirectSecurityConfig.java
+++ b/spring-security-modules/spring-security-web-boot-2/src/main/java/com/baeldung/loginredirect/LoginRedirectSecurityConfig.java
@@ -13,8 +13,9 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
-@Configuration
-@EnableWebSecurity
+//if you use xsd configuration (spring-security-login-redirect.xml) please comment the annotations from this class, because it will overlap with configuration from this class
+/*@Configuration
+@EnableWebSecurity*/
class LoginRedirectSecurityConfig {
private static final String LOGIN_USER = "/loginUser";
diff --git a/spring-security-modules/spring-security-web-boot-2/src/main/resources/spring-security-login-redirect.xml b/spring-security-modules/spring-security-web-boot-2/src/main/resources/spring-security-login-redirect.xml
index e711abce1f..8bdc35d055 100644
--- a/spring-security-modules/spring-security-web-boot-2/src/main/resources/spring-security-login-redirect.xml
+++ b/spring-security-modules/spring-security-web-boot-2/src/main/resources/spring-security-login-redirect.xml
@@ -6,11 +6,11 @@
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
- http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
- http://www.springframework.org/schema/security
- http://www.springframework.org/schema/security/spring-security-5.2.xsd
- http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd">
+ https://www.springframework.org/schema/mvc/spring-mvc.xsd
+ http://www.springframework.org/schema/security
+ https://www.springframework.org/schema/security/spring-security.xsd
+ http://www.springframework.org/schema/beans
+ https://www.springframework.org/schema/beans/spring-beans.xsd">
@@ -40,7 +40,7 @@
-
+
diff --git a/spring-security-modules/spring-security-web-mvc-custom/pom.xml b/spring-security-modules/spring-security-web-mvc-custom/pom.xml
index 41f24050b2..7f7f256196 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/pom.xml
+++ b/spring-security-modules/spring-security-web-mvc-custom/pom.xml
@@ -10,9 +10,9 @@
com.baeldung
- parent-spring-5
+ parent-spring-6
0.0.1-SNAPSHOT
- ../../parent-spring-5
+ ../../parent-spring-6
@@ -86,15 +86,15 @@
- javax.servlet
- javax.servlet-api
- ${javax.servlet-api.version}
+ jakarta.servlet
+ jakarta.servlet-api
+ ${jakarta.servlet-api.version}
provided
- javax.servlet
- jstl
- ${jstl.version}
+ jakarta.servlet.jsp.jstl
+ jakarta.servlet.jsp.jstl-api
+ ${jakarta.jstl-api.version}
runtime
@@ -127,9 +127,9 @@
test
- javax.annotation
- javax.annotation-api
- ${javax.annotation-api.version}
+ jakarta.annotation
+ jakarta.annotation-api
+ ${jakarta.annotation-api.version}
@@ -173,7 +173,10 @@
1.6.1
- 1.3.2
+ 3.0.0-M1
+ 6.1.0-M1
+ 3.0.0
+ 6.2.1
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java
index 728445952e..9422e7032d 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/security/MySimpleUrlAuthenticationSuccessHandler.java
@@ -5,9 +5,9 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/spring/MyUserDetailsService.java b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/spring/MyUserDetailsService.java
index ee80ad12d7..9cf0779e73 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/spring/MyUserDetailsService.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/spring/MyUserDetailsService.java
@@ -7,7 +7,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
-import javax.annotation.PostConstruct;
+import jakarta.annotation.PostConstruct;
import java.util.*;
@Service
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/spring/SecSecurityConfig.java b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/spring/SecSecurityConfig.java
index d5e83a1110..d228dec49f 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/spring/SecSecurityConfig.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/spring/SecSecurityConfig.java
@@ -6,6 +6,7 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@@ -40,29 +41,20 @@ public class SecSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers("/anonymous*")
+ http.authorizeHttpRequests(auth -> auth.requestMatchers("/anonymous*")
.anonymous()
- .antMatchers("/login*")
+ .requestMatchers("/login*")
.permitAll()
.anyRequest()
- .authenticated()
- .and()
- .formLogin()
- .loginPage("/login.html")
- .loginProcessingUrl("/login")
- .successHandler(myAuthenticationSuccessHandler())
- .failureUrl("/login.html?error=true")
- .and()
- .logout()
- .deleteCookies("JSESSIONID")
- .and()
- .rememberMe()
- .key("uniqueAndSecret")
- .tokenValiditySeconds(86400)
- .and()
- .csrf()
- .disable();
+ .authenticated())
+ .formLogin(formLogin -> formLogin.loginPage("/login.html")
+ .loginProcessingUrl("/login")
+ .successHandler(myAuthenticationSuccessHandler())
+ .failureUrl("/login.html?error=true"))
+ .rememberMe(rememberMe -> rememberMe.key("uniqueAndSecret")
+ .tokenValiditySeconds(86400))
+ .logout(logout -> logout.deleteCookies("JSESSIONID"))
+ .csrf(AbstractHttpConfigurer::disable);
return http.build();
}
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/controller/FooController.java b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/controller/FooController.java
index 6f9268c976..8d5016cdc4 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/controller/FooController.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/controller/FooController.java
@@ -5,7 +5,7 @@ import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import java.util.Arrays;
import java.util.List;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
import com.baeldung.web.dto.Foo;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/controller/LoginController.java b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/controller/LoginController.java
index 8a823cdf7e..5f73f9ea8f 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/controller/LoginController.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/controller/LoginController.java
@@ -1,10 +1,5 @@
package com.baeldung.web.controller;
-import javax.annotation.Resource;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpSession;
-
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -15,6 +10,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
+
@Controller
@RequestMapping(value = "/custom")
public class LoginController {
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/LoggerInterceptor.java b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/LoggerInterceptor.java
index 669e4cb3c5..78f3de6c20 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/LoggerInterceptor.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/LoggerInterceptor.java
@@ -2,8 +2,8 @@ package com.baeldung.web.interceptor;
import java.util.Enumeration;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/SessionTimerInterceptor.java b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/SessionTimerInterceptor.java
index e7decc262f..d914b564fe 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/SessionTimerInterceptor.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/SessionTimerInterceptor.java
@@ -1,8 +1,8 @@
package com.baeldung.web.interceptor;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/UserInterceptor.java b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/UserInterceptor.java
index ad80571be6..152d9f9864 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/UserInterceptor.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/main/java/com/baeldung/web/interceptor/UserInterceptor.java
@@ -1,8 +1,8 @@
package com.baeldung.web.interceptor;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/csrf/CsrfAbstractIntegrationTest.java b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/csrf/CsrfAbstractIntegrationTest.java
index 97972b7358..fc289015a7 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/csrf/CsrfAbstractIntegrationTest.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/csrf/CsrfAbstractIntegrationTest.java
@@ -3,7 +3,7 @@ package com.baeldung.security.csrf;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
-import javax.servlet.Filter;
+import jakarta.servlet.Filter;
import com.baeldung.web.dto.Foo;
import org.junit.Before;
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/ManualSecurityConfig.java b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/ManualSecurityConfig.java
index d2553ac2a8..0aba57ea73 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/ManualSecurityConfig.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/ManualSecurityConfig.java
@@ -3,11 +3,13 @@ package com.baeldung.security.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+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.annotation.web.configuration.WebSecurityCustomizer;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@@ -15,7 +17,7 @@ import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableMethodSecurity
public class ManualSecurityConfig {
@Bean
@@ -34,7 +36,7 @@ public class ManualSecurityConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
- .antMatchers("/resources/**");
+ .requestMatchers("/resources/**");
}
@Bean
@@ -45,20 +47,13 @@ public class ManualSecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .mvcMatchers("/custom/login")
- .permitAll()
- .anyRequest()
- .authenticated()
- .and()
- .httpBasic()
- .and()
- .headers()
- .cacheControl()
- .disable()
- .and()
- .csrf()
- .disable();
+ http.csrf(AbstractHttpConfigurer::disable)
+ .httpBasic(Customizer.withDefaults())
+ .headers(headers -> headers.cacheControl((cacheControl) -> cacheControl.disable()))
+ .authorizeHttpRequests(auth -> auth.requestMatchers("/custom/login")
+ .permitAll()
+ .anyRequest()
+ .authenticated());
return http.build();
}
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/ManualSecurityIntegrationTest.java b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/ManualSecurityIntegrationTest.java
index 002ba8df3e..bf21c3a402 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/ManualSecurityIntegrationTest.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/ManualSecurityIntegrationTest.java
@@ -3,7 +3,7 @@ package com.baeldung.security.spring;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpSession;
import com.baeldung.spring.MvcConfig;
import org.junit.Before;
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithCsrfConfig.java b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithCsrfConfig.java
index a1a7c8bc54..a1070be9cd 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithCsrfConfig.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithCsrfConfig.java
@@ -3,11 +3,14 @@ package com.baeldung.security.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+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.annotation.web.configuration.WebSecurityCustomizer;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@@ -15,7 +18,7 @@ import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableMethodSecurity
public class SecurityWithCsrfConfig {
@Bean
@@ -40,22 +43,17 @@ public class SecurityWithCsrfConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
- .antMatchers("/resources/**");
+ .requestMatchers("/resources/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers("/auth/admin/*")
- .hasAnyRole("ROLE_ADMIN")
- .anyRequest()
- .authenticated()
- .and()
- .httpBasic()
- .and()
- .headers()
- .cacheControl()
- .disable();
+ http.httpBasic(Customizer.withDefaults())
+ .headers(headers -> headers.cacheControl((cacheControl) -> cacheControl.disable()))
+ .authorizeHttpRequests(auth -> auth.requestMatchers("/auth/admin/*")
+ .hasAnyRole("ADMIN")
+ .anyRequest()
+ .authenticated());
return http.build();
}
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithCsrfCookieConfig.java b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithCsrfCookieConfig.java
index a34fa4c704..d10606eba0 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithCsrfCookieConfig.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithCsrfCookieConfig.java
@@ -3,8 +3,9 @@ package com.baeldung.security.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+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.annotation.web.configuration.WebSecurityCustomizer;
@@ -16,7 +17,7 @@ import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableMethodSecurity
public class SecurityWithCsrfCookieConfig {
@Bean
@@ -41,26 +42,19 @@ public class SecurityWithCsrfCookieConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
- .antMatchers("/resources/**");
+ .requestMatchers("/resources/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers("/auth/admin/*")
- .hasAnyRole("ROLE_ADMIN")
- .anyRequest()
- .authenticated()
- .and()
- .httpBasic()
- .and()
- .headers()
- .cacheControl()
- .disable()
- // Stateless API CSRF configuration
- .and()
- .csrf()
- .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
+ // Stateless API CSRF configuration
+ http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
+ .httpBasic(Customizer.withDefaults())
+ .headers(headers -> headers.cacheControl((cacheControl) -> cacheControl.disable()))
+ .authorizeHttpRequests(auth -> auth.requestMatchers("/auth/admin/*")
+ .hasAnyRole("ADMIN")
+ .anyRequest()
+ .authenticated());
return http.build();
}
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithoutCsrfConfig.java b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithoutCsrfConfig.java
index 20df0f4d6d..a70c71f3b4 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithoutCsrfConfig.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/security/spring/SecurityWithoutCsrfConfig.java
@@ -3,11 +3,13 @@ package com.baeldung.security.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
-import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+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.annotation.web.configuration.WebSecurityCustomizer;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@@ -15,7 +17,7 @@ import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
-@EnableGlobalMethodSecurity(prePostEnabled = true)
+@EnableMethodSecurity
public class SecurityWithoutCsrfConfig {
@Bean
@@ -28,11 +30,11 @@ public class SecurityWithoutCsrfConfig {
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withUsername("user1")
.password("user1Pass")
- .authorities("ROLE_USER")
+ .authorities("USER")
.build();
UserDetails admin = User.withUsername("admin")
.password("adminPass")
- .authorities("ROLE_ADMIN")
+ .authorities("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
@@ -40,25 +42,18 @@ public class SecurityWithoutCsrfConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
- .antMatchers("/resources/**");
+ .requestMatchers("/resources/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers("/auth/admin/*")
- .hasAnyRole("ROLE_ADMIN")
- .anyRequest()
- .authenticated()
- .and()
- .httpBasic()
- .and()
- .headers()
- .cacheControl()
- .disable()
- .and()
- .csrf()
- .disable();
+ http.csrf(AbstractHttpConfigurer::disable)
+ .httpBasic(Customizer.withDefaults())
+ .headers(headers -> headers.cacheControl((cacheControl) -> cacheControl.disable()))
+ .authorizeHttpRequests(auth -> auth.requestMatchers("/auth/admin/*")
+ .hasAnyRole("ADMIN")
+ .anyRequest()
+ .authenticated());
return http.build();
}
diff --git a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java
index 873c28c6a2..c17769e081 100644
--- a/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java
+++ b/spring-security-modules/spring-security-web-mvc-custom/src/test/java/com/baeldung/web/interceptor/SessionTimerInterceptorIntegrationTest.java
@@ -3,7 +3,7 @@ package com.baeldung.web.interceptor;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-import javax.servlet.http.HttpSession;
+import jakarta.servlet.http.HttpSession;
import com.baeldung.security.spring.SecurityWithoutCsrfConfig;
import com.baeldung.spring.MvcConfig;
diff --git a/spring-security-modules/spring-security-web-persistent-login/pom.xml b/spring-security-modules/spring-security-web-persistent-login/pom.xml
index beafc10ffd..cdd3441ddc 100644
--- a/spring-security-modules/spring-security-web-persistent-login/pom.xml
+++ b/spring-security-modules/spring-security-web-persistent-login/pom.xml
@@ -10,9 +10,9 @@
com.baeldung
- parent-spring-5
+ parent-spring-6
0.0.1-SNAPSHOT
- ../../parent-spring-5
+ ../../parent-spring-6
@@ -20,17 +20,17 @@
org.springframework.security
spring-security-web
- ${org.springframework.security.version}
+ ${spring.version}
org.springframework.security
spring-security-config
- ${org.springframework.security.version}
+ ${spring.version}
org.springframework.security
spring-security-taglibs
- ${org.springframework.security.version}
+ ${spring.version}
org.springframework
@@ -91,15 +91,15 @@
- javax.servlet
- javax.servlet-api
- ${javax.servlet-api.version}
+ jakarta.servlet
+ jakarta.servlet-api
+ ${jakarta.servlet-api}
provided
- javax.servlet
- jstl
- ${jstl.version}
+ jakarta.servlet.jsp.jstl
+ jakarta.servlet.jsp.jstl-api
+ ${jakarta.servlet.jsp.jstl-api}
runtime
@@ -167,13 +167,12 @@