diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
index 7ff8be4..04a1a0c 100644
--- a/.github/workflows/unit-test.yml
+++ b/.github/workflows/unit-test.yml
@@ -8,7 +8,7 @@ jobs:
build:
runs-on: ubuntu-latest
env:
- SPRING_BASE_COMMONS_VERSION: 2.3.0
+ SPRING_BASE_COMMONS_VERSION: 2.4.1
steps:
- uses: actions/checkout@v4
- name: Set up JDK 25
diff --git a/Dockerfile b/Dockerfile
index 40c2739..ac0fc50 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -17,7 +17,7 @@ ENV APP_NAME=app.jar
ENV DEPS_FILE=deps.info
# Change this when there is an update
-ENV SPRING_BASE_COMMONS_VERSION=2.3.0
+ENV SPRING_BASE_COMMONS_VERSION=2.4.1
# Clone the spring-base-commons repository
RUN git clone --depth 1 --branch ${SPRING_BASE_COMMONS_VERSION} https://github.com/vulinh64/spring-base-commons.git
diff --git a/create-data-classes.cmd b/create-data-classes.cmd
index 455afae..747955e 100644
--- a/create-data-classes.cmd
+++ b/create-data-classes.cmd
@@ -1,6 +1,6 @@
@echo off
-SET SPRING_BASE_COMMONS_VERSION=2.3.0
+SET SPRING_BASE_COMMONS_VERSION=2.4.1
IF EXIST .\build\spring-base-commons rmdir /s /q .\build\spring-base-commons
diff --git a/create-data-classes.sh b/create-data-classes.sh
index 59cd689..f3aead6 100755
--- a/create-data-classes.sh
+++ b/create-data-classes.sh
@@ -2,7 +2,7 @@
set -e
-SPRING_BASE_COMMONS_VERSION=2.3.0
+SPRING_BASE_COMMONS_VERSION=2.4.1
if [ -d "./build/spring-base-commons" ]; then
rm -rf ./build/spring-base-commons
diff --git a/pom.xml b/pom.xml
index 8987ae6..2caccf6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,7 +68,7 @@
4.2.9.Final
- 2.3.0
+ 2.4.1
diff --git a/src/main/java/com/vulinh/SpringBaseProjectApplication.java b/src/main/java/com/vulinh/SpringBaseProjectApplication.java
index 6d56edb..0487993 100644
--- a/src/main/java/com/vulinh/SpringBaseProjectApplication.java
+++ b/src/main/java/com/vulinh/SpringBaseProjectApplication.java
@@ -1,5 +1,6 @@
package com.vulinh;
+import com.vulinh.aspect.ExecutionTimeAspect;
import com.vulinh.configuration.AuditorConfiguration;
import com.vulinh.configuration.data.ApplicationProperties;
import lombok.AccessLevel;
@@ -7,6 +8,7 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.data.web.config.EnableSpringDataWebSupport.PageSerializationMode;
@@ -18,6 +20,7 @@
@EnableConfigurationProperties(ApplicationProperties.class)
@EnableAsync
@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@Import(ExecutionTimeAspect.class)
class SpringBaseProjectApplication {
static void main(String[] args) {
diff --git a/src/main/java/com/vulinh/configuration/SecurityConfiguration.java b/src/main/java/com/vulinh/configuration/SecurityConfiguration.java
index e49698a..906e4de 100644
--- a/src/main/java/com/vulinh/configuration/SecurityConfiguration.java
+++ b/src/main/java/com/vulinh/configuration/SecurityConfiguration.java
@@ -6,6 +6,8 @@
import com.vulinh.configuration.data.ApplicationProperties.SecurityProperties;
import com.vulinh.data.constant.UserRole;
import com.vulinh.utils.JwtUtils;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -13,7 +15,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
-import org.springframework.security.config.Customizer;
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;
@@ -24,6 +25,7 @@
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.servlet.HandlerExceptionResolver;
@Configuration
@EnableWebSecurity
@@ -31,6 +33,9 @@
@Slf4j
public class SecurityConfiguration {
+ // One of the best and the most elegant ways to handle exceptions in Spring Security filters
+ private final HandlerExceptionResolver handlerExceptionResolver;
+
static final String ROLE_ADMIN_NAME = UserRole.ADMIN.name();
@Bean
@@ -47,7 +52,6 @@ public SecurityFilterChain securityFilterChain(
xssConfig -> xssConfig.headerValue(HeaderValue.ENABLED_MODE_BLOCK))
.contentSecurityPolicy(cps -> cps.policyDirectives("script-src 'self'")))
.csrf(AbstractHttpConfigurer::disable)
- .cors(Customizer.withDefaults())
.sessionManagement(
sessionManagementConfigurer ->
sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
@@ -57,12 +61,17 @@ public SecurityFilterChain securityFilterChain(
.cors(corsConfigurer -> corsConfigurer.configurationSource(createCorsFilter()))
.oauth2ResourceServer(
oAuth2ResourceServerProperties ->
- oAuth2ResourceServerProperties.jwt(
- jwtConfigurer ->
- jwtConfigurer.jwtAuthenticationConverter(
- jwt ->
- JwtUtils.parseAuthoritiesByCustomClaims(
- jwt, security.clientName()))))
+ oAuth2ResourceServerProperties
+ // Return something to client rather than a blank 403 page
+ .accessDeniedHandler(this::delegateToHandlerExceptionResolver)
+ // Return something to client rather than a blank 401 page
+ .authenticationEntryPoint(this::delegateToHandlerExceptionResolver)
+ .jwt(
+ jwtConfigurer ->
+ jwtConfigurer.jwtAuthenticationConverter(
+ jwt ->
+ JwtUtils.parseAuthoritiesByCustomClaims(
+ jwt, security.clientName()))))
.build();
}
@@ -86,6 +95,11 @@ CorsConfigurationSource createCorsFilter() {
return source;
}
+ private void delegateToHandlerExceptionResolver(
+ HttpServletRequest request, HttpServletResponse response, Exception exception) {
+ handlerExceptionResolver.resolveException(request, response, null, exception);
+ }
+
static void configureAuthorizeHttpRequestCustomizer(
AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry
authorizeHttpRequestsCustomizer,
diff --git a/src/main/java/com/vulinh/controller/impl/SubscriptionController.java b/src/main/java/com/vulinh/controller/impl/SubscriptionController.java
index 5445012..e2206e4 100644
--- a/src/main/java/com/vulinh/controller/impl/SubscriptionController.java
+++ b/src/main/java/com/vulinh/controller/impl/SubscriptionController.java
@@ -3,7 +3,7 @@
import module java.base;
import com.vulinh.controller.api.SubscriptionAPI;
-import com.vulinh.service.event.UserSubscriptionService;
+import com.vulinh.service.UserSubscriptionService;
import com.vulinh.utils.ResponseUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
diff --git a/src/main/java/com/vulinh/exception/AuthorizationException.java b/src/main/java/com/vulinh/exception/AuthorizationException.java
index 72880e5..4dcefa4 100644
--- a/src/main/java/com/vulinh/exception/AuthorizationException.java
+++ b/src/main/java/com/vulinh/exception/AuthorizationException.java
@@ -24,7 +24,7 @@ public class AuthorizationException extends ApplicationException {
/// @return A new [AuthorizationException] instance
public static AuthorizationException invalidAuthorization(Object... args) {
return invalidAuthorization(
- "Invalid user authorization", ServiceErrorCode.MESSAGE_INVALID_AUTHORIZATION, args);
+ "Invalid user authorization", ServiceErrorCode.MESSAGE_INVALID_AUTHENTICATION, args);
}
/// Creates an [AuthorizationException] with a custom message and error code.
diff --git a/src/main/java/com/vulinh/exception/GlobalExceptionHandler.java b/src/main/java/com/vulinh/exception/GlobalExceptionHandler.java
index d065d12..e187636 100644
--- a/src/main/java/com/vulinh/exception/GlobalExceptionHandler.java
+++ b/src/main/java/com/vulinh/exception/GlobalExceptionHandler.java
@@ -6,11 +6,13 @@
import com.vulinh.data.dto.response.GenericResponse.ResponseCreator;
import com.vulinh.locale.LocalizationSupport;
import com.vulinh.locale.ServiceErrorCode;
+import com.vulinh.utils.validator.ApplicationError;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageConversionException;
+import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@@ -112,6 +114,19 @@ GenericResponse