From aaf5d2dbd828cc0e670ee69eba2139885ab56c60 Mon Sep 17 00:00:00 2001 From: Kshitij <160704796+kshitij-ka@users.noreply.github.com> Date: Thu, 3 Jul 2025 02:43:56 +0530 Subject: [PATCH] Add JWT authentication filter to secure protected routes - Intercepts all requests and checks for Bearer token. - Validates token signature and expiry using JwtService. - Loads user from DB and sets authentication context. - Sends 401 Unauthorized if token is missing, invalid, or expired. --- .../config/SecurityConfig.java | 22 +++--- .../security/JwtAuthenticationFilter.java | 70 +++++++++++++++++++ 2 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/skycrate/backend/skycrateBackend/security/JwtAuthenticationFilter.java diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/config/SecurityConfig.java b/src/main/java/com/skycrate/backend/skycrateBackend/config/SecurityConfig.java index 97ce5aa..bdd4fc5 100644 --- a/src/main/java/com/skycrate/backend/skycrateBackend/config/SecurityConfig.java +++ b/src/main/java/com/skycrate/backend/skycrateBackend/config/SecurityConfig.java @@ -1,5 +1,6 @@ package com.skycrate.backend.skycrateBackend.config; +import com.skycrate.backend.skycrateBackend.security.JwtAuthenticationFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; @@ -7,22 +8,24 @@ import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.header.writers.HstsHeaderWriter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration public class SecurityConfig { private final AuthenticationProvider authenticationProvider; + private final JwtAuthenticationFilter jwtAuthenticationFilter; - public SecurityConfig(AuthenticationProvider authenticationProvider) { + public SecurityConfig(AuthenticationProvider authenticationProvider, + JwtAuthenticationFilter jwtAuthenticationFilter) { this.authenticationProvider = authenticationProvider; + this.jwtAuthenticationFilter = jwtAuthenticationFilter; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .csrf(csrf -> csrf.disable()) // if using JWT; enable if using sessions + .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authenticationProvider(authenticationProvider) .authorizeHttpRequests(auth -> auth @@ -38,13 +41,10 @@ public class SecurityConfig { .includeSubDomains(true) .maxAgeInSeconds(31536000) ) - .xssProtection(xss -> xss - .block(true) - ) - .frameOptions(frame -> frame - .deny() - ) - ); + .xssProtection(xss -> xss.block(true)) + .frameOptions(frame -> frame.deny()) + ) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } diff --git a/src/main/java/com/skycrate/backend/skycrateBackend/security/JwtAuthenticationFilter.java b/src/main/java/com/skycrate/backend/skycrateBackend/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..f821a9c --- /dev/null +++ b/src/main/java/com/skycrate/backend/skycrateBackend/security/JwtAuthenticationFilter.java @@ -0,0 +1,70 @@ +package com.skycrate.backend.skycrateBackend.security; + +import com.skycrate.backend.skycrateBackend.repository.UserRepository; +import com.skycrate.backend.skycrateBackend.entity.User; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtService jwtService; + private final UserRepository userRepository; + + public JwtAuthenticationFilter(JwtService jwtService, UserRepository userRepository) { + this.jwtService = jwtService; + this.userRepository = userRepository; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + + final String authHeader = request.getHeader("Authorization"); + final String jwt; + final String userEmail; + + if (!StringUtils.hasText(authHeader) || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + + jwt = authHeader.substring(7); + try { + userEmail = jwtService.extractUsername(jwt); + } catch (Exception e) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().write("Invalid JWT token"); + return; + } + + if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { + User user = userRepository.findByEmail(userEmail).orElse(null); + + if (user != null && jwtService.isTokenValid(jwt, user)) { + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + user, null, user.getAuthorities()); + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authToken); + } else { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().write("Expired or invalid JWT"); + return; + } + } + + filterChain.doFilter(request, response); + } +} \ No newline at end of file