22
33import java .io .IOException ;
44import java .util .Arrays ;
5+ import java .util .HashMap ;
6+ import java .util .Map ;
7+ import java .util .Set ;
58
69import org .slf4j .Logger ;
710import org .slf4j .LoggerFactory ;
@@ -23,6 +26,7 @@ public class JwtUserIdValidationFilter implements Filter {
2326 private final Logger logger = LoggerFactory .getLogger (this .getClass ().getName ());
2427 private final String allowedOrigins ;
2528
29+
2630 public JwtUserIdValidationFilter (JwtAuthenticationUtil jwtAuthenticationUtil ,
2731 String allowedOrigins ) {
2832 this .jwtAuthenticationUtil = jwtAuthenticationUtil ;
@@ -36,27 +40,63 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
3640 HttpServletResponse response = (HttpServletResponse ) servletResponse ;
3741
3842 String origin = request .getHeader ("Origin" );
43+ String method = request .getMethod ();
44+ String uri = request .getRequestURI ();
3945
4046 logger .debug ("Incoming Origin: {}" , origin );
47+ logger .debug ("Request Method: {}" , method );
48+ logger .debug ("Request URI: {}" , uri );
4149 logger .debug ("Allowed Origins Configured: {}" , allowedOrigins );
42- logger .info ("Add server authorization header to response" );
50+
51+ // STEP 1: STRICT Origin Validation - Block unauthorized origins immediately
52+ // For OPTIONS requests, Origin header is required (CORS preflight)
53+ if ("OPTIONS" .equalsIgnoreCase (method )) {
54+ if (origin == null ) {
55+ logger .warn ("BLOCKED - OPTIONS request without Origin header | Method: {} | URI: {}" , method , uri );
56+ response .sendError (HttpServletResponse .SC_FORBIDDEN , "OPTIONS request requires Origin header" );
57+ return ;
58+ }
59+ if (!isOriginAllowed (origin )) {
60+ logger .warn ("BLOCKED - Unauthorized Origin | Origin: {} | Method: {} | URI: {}" , origin , method , uri );
61+ response .sendError (HttpServletResponse .SC_FORBIDDEN , "Origin not allowed" );
62+ return ;
63+ }
64+ } else {
65+ // For non-OPTIONS requests, validate origin if present
66+ if (origin != null && !isOriginAllowed (origin )) {
67+ logger .warn ("BLOCKED - Unauthorized Origin | Origin: {} | Method: {} | URI: {}" , origin , method , uri );
68+ response .sendError (HttpServletResponse .SC_FORBIDDEN , "Origin not allowed" );
69+ return ;
70+ }
71+ }
72+
73+ // Determine request path/context for later checks
74+ String path = request .getRequestURI ();
75+ String contextPath = request .getContextPath ();
76+
77+ // STEP 3: Add CORS Headers (only for validated origins)
4378 if (origin != null && isOriginAllowed (origin )) {
44- response .setHeader ("Access-Control-Allow-Origin" , origin );
45- response .setHeader ("Access-Control-Allow-Methods" , "GET, POST, PUT, DELETE, OPTIONS" );
46- response .setHeader ("Access-Control-Allow-Headers" , "Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization" );
79+ response .setHeader ("Access-Control-Allow-Origin" , origin ); // Never use wildcard
80+ response .setHeader ("Access-Control-Allow-Methods" , "GET, POST, PUT, PATCH, DELETE, OPTIONS" );
81+ response .setHeader ("Access-Control-Allow-Headers" ,
82+ "Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization" );
4783 response .setHeader ("Access-Control-Allow-Credentials" , "true" );
48- } else {
49- logger .warn ("Origin [{}] is NOT allowed. CORS headers NOT added. " , origin );
84+ response . setHeader ( "Access-Control-Max-Age" , "3600" );
85+ logger .info ("Origin Validated | Origin: {} | Method: {} | URI: {} " , origin , method , uri );
5086 }
5187
52- if ("OPTIONS" .equalsIgnoreCase (request .getMethod ())) {
53- logger .info ("OPTIONS request - skipping JWT validation" );
88+ // STEP 4: Handle OPTIONS Preflight Request
89+ if ("OPTIONS" .equalsIgnoreCase (method )) {
90+ // OPTIONS (preflight) - respond with full allowed methods
91+ response .setHeader ("Access-Control-Allow-Origin" , origin );
92+ response .setHeader ("Access-Control-Allow-Methods" , "GET, POST, PUT, PATCH, DELETE, OPTIONS" );
93+ response .setHeader ("Access-Control-Allow-Headers" ,
94+ "Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization" );
95+ response .setHeader ("Access-Control-Allow-Credentials" , "true" );
5496 response .setStatus (HttpServletResponse .SC_OK );
5597 return ;
5698 }
5799
58- String path = request .getRequestURI ();
59- String contextPath = request .getContextPath ();
60100 logger .info ("JwtUserIdValidationFilter invoked for path: " + path );
61101
62102 // Log cookies for debugging
@@ -73,8 +113,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
73113 }
74114
75115 // Log headers for debugging
76- String jwtTokenFromHeader = request .getHeader ("Jwttoken" );
77- logger .info ("JWT token from header: " );
116+ logger .debug ("JWT token from header: {}" , request .getHeader ("Jwttoken" ) != null ? "present" : "not present" );
78117
79118 // Skip authentication for public endpoints
80119 if (shouldSkipAuthentication (path , contextPath )) {
@@ -138,14 +177,15 @@ private boolean isOriginAllowed(String origin) {
138177 .anyMatch (pattern -> {
139178 String regex = pattern
140179 .replace ("." , "\\ ." )
141- .replace ("*" , ".*" )
142- .replace ("http://localhost:.*" , "http://localhost:\\ d+" ); // special case for wildcard port
180+ .replace ("*" , ".*" );
143181
144182 boolean matched = origin .matches (regex );
145183 return matched ;
146184 });
147185 }
148186
187+
188+
149189 private boolean isMobileClient (String userAgent ) {
150190 if (userAgent == null )
151191 return false ;
0 commit comments