This commit is contained in:
DarioGii 2025-07-09 16:28:44 +01:00 committed by Dario Ghunney Ware
parent 13e985657f
commit 2fb60eee8d
5 changed files with 73 additions and 28 deletions

View File

@ -10,7 +10,6 @@ import java.util.Properties;
import java.util.function.Predicate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -151,9 +150,8 @@ public class AppConfig {
@Bean(name = "activeSecurity")
public boolean missingActiveSecurity() {
return ClassUtils.isPresent(
"stirling.software.proprietary.security.configuration.SecurityConfiguration",
this.getClass().getClassLoader()
);
"stirling.software.proprietary.security.configuration.SecurityConfiguration",
this.getClass().getClassLoader());
}
@Bean(name = "directoryFilter")

View File

@ -53,17 +53,21 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter {
String jwtToken = jwtService.extractTokenFromRequest(request);
if (jwtToken == null) {
// Special handling for root path - redirect to login instead of 401
// Redirect to /login instead of 401
if ("/".equals(request.getRequestURI())
&& "GET".equalsIgnoreCase(request.getMethod())) {
response.sendRedirect("/login");
return;
}
throw new AuthenticationFailureException("JWT is missing from request");
sendUnauthorizedResponse(response, "JWT is missing from the request");
return;
}
if (!jwtService.validateToken(jwtToken)) {
throw new AuthenticationFailureException("JWT is invalid or expired");
try {
jwtService.validateToken(jwtToken);
} catch (AuthenticationFailureException e) {
sendUnauthorizedResponse(response, e.getMessage());
return;
}
String tokenUsername = jwtService.extractUsername(jwtToken);
@ -134,4 +138,27 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter {
return false;
}
private void sendUnauthorizedResponse(HttpServletResponse response, String message)
throws IOException {
int unauthorized = HttpServletResponse.SC_UNAUTHORIZED;
response.setStatus(unauthorized);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
String jsonResponse =
String.format(
"""
{
"error": "Unauthorized",
"message": "%s",
"status": %d
}
""",
message, unauthorized);
response.getWriter().write(jsonResponse);
response.getWriter().flush();
}
}

View File

@ -6,4 +6,8 @@ public class AuthenticationFailureException extends AuthenticationException {
public AuthenticationFailureException(String message) {
super(message);
}
public AuthenticationFailureException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -26,6 +26,7 @@ import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.proprietary.security.model.exception.AuthenticationFailureException;
@Slf4j
@Service
@ -69,27 +70,29 @@ public class JWTService implements JWTServiceInterface {
}
@Override
public boolean validateToken(String token) {
public void validateToken(String token) {
if (!isJwtEnabled()) {
return false;
throw new IllegalStateException("JWT is not enabled");
}
try {
Jwts.parser().verifyWith(keyPair.getPublic()).build().parseSignedClaims(token);
return true;
extractAllClaimsFromToken(token);
} catch (SignatureException e) {
log.warn("Invalid JWT signature: {}", e.getMessage());
log.warn("Invalid signature: {}", e.getMessage());
throw new AuthenticationFailureException("Invalid signature", e);
} catch (MalformedJwtException e) {
log.warn("Invalid JWT token: {}", e.getMessage());
log.warn("Invalid token: {}", e.getMessage());
throw new AuthenticationFailureException("Invalid token", e);
} catch (ExpiredJwtException e) {
log.warn("JWT token is expired: {}", e.getMessage());
log.warn("The token has expired: {}", e.getMessage());
throw new AuthenticationFailureException("The token has expired", e);
} catch (UnsupportedJwtException e) {
log.warn("JWT token is unsupported: {}", e.getMessage());
log.warn("The token is unsupported: {}", e.getMessage());
throw new AuthenticationFailureException("The token is unsupported", e);
} catch (IllegalArgumentException e) {
log.warn("JWT claims string is empty: {}", e.getMessage());
log.warn("Claims are empty: {}", e.getMessage());
throw new AuthenticationFailureException("Claims are empty", e);
}
return false;
}
@Override
@ -118,15 +121,28 @@ public class JWTService implements JWTServiceInterface {
}
private Claims extractAllClaimsFromToken(String token) {
if (!isJwtEnabled()) {
throw new IllegalStateException("JWT is not enabled");
try {
return Jwts.parser()
.verifyWith(keyPair.getPublic())
.build()
.parseSignedClaims(token)
.getPayload();
} catch (SignatureException e) {
log.warn("Invalid JWT signature: {}", e.getMessage());
throw new AuthenticationFailureException("Invalid JWT signature", e);
} catch (MalformedJwtException e) {
log.warn("Invalid JWT token: {}", e.getMessage());
throw new AuthenticationFailureException("Invalid JWT token", e);
} catch (ExpiredJwtException e) {
log.warn("JWT token is expired: {}", e.getMessage());
throw new AuthenticationFailureException("JWT token is expired", e);
} catch (UnsupportedJwtException e) {
log.warn("JWT token is unsupported: {}", e.getMessage());
throw new AuthenticationFailureException("JWT token is unsupported", e);
} catch (IllegalArgumentException e) {
log.warn("JWT claims are empty: {}", e.getMessage());
throw new AuthenticationFailureException("JWT claims are empty", e);
}
return Jwts.parser()
.verifyWith(keyPair.getPublic())
.build()
.parseSignedClaims(token)
.getPayload();
}
@Override

View File

@ -34,7 +34,7 @@ public interface JWTServiceInterface {
* @param token the JWT token to validate
* @return true if token is valid, false otherwise
*/
boolean validateToken(String token);
void validateToken(String token);
/**
* Extract username from JWT token