Search code examples
javaspringspring-bootspring-security

After implementing Spring security Authentication my app doesn't return correct bodies just empty 200 HTTP responses


I have implemented some easy method to authenticate users in springboot which returns jwt and validates users but the app stopped giving responses with actual bodies and started sending empty 200 responses any clue why this might be? According to the debugger the program never ends up on the Request I was trying to call but authorization with the jwt is always succesfull

my classes

JWTService

@Service
public class JwtService {
private static final String SECRET_KEY = "32bitkey";
public String extractEmail(String token) {
return exctractClaim(token, Claims::getSubject);
}

    public <T> T exctractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }
    
    public String generateToken(
            UserDetails userDetails) {
        return generateToken(Map.of(), userDetails);
    }
    public String generateToken(
            Map<String, Object> extraClaims,
            UserDetails userDetails) {
        return Jwts
                .builder()
                .setClaims(extraClaims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(getSigningKey(), SignatureAlgorithm.HS256)
                .compact();
    }
    
    public boolean isTokenValid(String token, UserDetails userDetails) {
        final String email = extractEmail(token);
        return email.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }
    
    public boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }
    
    private Date extractExpiration(String token) {
       return exctractClaim(token, Claims::getExpiration);
    }
    
    private Claims extractAllClaims(String token) {
        return Jwts
                .parserBuilder()
                .setSigningKey(getSigningKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }
    private Key getSigningKey() {
        byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
        return Keys.hmacShaKeyFor(keyBytes);
    }

Security filter chain

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
        security
                .csrf()
                .disable()
                .authorizeHttpRequests()
                .requestMatchers(
                        "/fixture/getFixturesBySportAndDate",
                        "/user/register",
                        "/user/authenticate",
                        "/league/getLeaguesByFixturePlayedAtDateInSport",
                        "/team/fillTeamsHockey",
                        "/fixture/fillFixturesHockey",
                        "/fixture/fillFixturesBasketball")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authenticationProvider(authenticationProvider)
                .addFilterBefore(jwtAuthetificationFilter, UsernamePasswordAuthenticationFilter.class);

        return security.build();
    }

JwtAuthFilter

@Component
@RequiredArgsConstructor
public class JwtAuthetificationFilter extends OncePerRequestFilter {
    private final JwtService jwtService;
    @Autowired
    private UserDetailsService userDetailsService;
    @Override
    protected void doFilterInternal(
            @NonNull HttpServletRequest request,
            @NonNull HttpServletResponse response,
            @NonNull FilterChain filterChain
    ) throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");
        final String jwt;
        final String email;
        if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
            filterChain.doFilter(request, response);
            return;
        }
        jwt = authorizationHeader.substring(7);
        email = jwtService.extractEmail(jwt);
        if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
            UserDetails userDetails = userDetailsService.loadUserByUsername(email);
            if (jwtService.isTokenValid(jwt, userDetails)){
                UsernamePasswordAuthenticationToken authenticationToken
                        = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authenticationToken.setDetails(
                        new WebAuthenticationDetailsSource().buildDetails(request)
                );
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
    }

Application Config

@Configuration @RequiredArgsConstructor
public class ApplicationConfig {

    private final UserRepository userRepository;

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> userRepository.findByEmail(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        };

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration cfg) throws Exception {
         return cfg.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
       return new BCryptPasswordEncoder();
    }

Example request

    @GetMapping(value = "/getUserInfo")
    public ResponseEntity<User> getUserInfo(HttpServletRequest request){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String email = authentication.getName();
        User user = userRepository.findByEmail(email).get();
        return ResponseEntity.ok(user);
    }

I tried changing basically everything and debugging the info but couldn't get it done because it just gave me the answer that the request never executes


Solution

  • I haven't tried running your code at all, but from first glance it looks like you're not calling up the filter chain after setting the security context's authentication within JwtAuthenticationFilter (although you do if there is no authorization header).

    Try adjusting this:

    ...
            if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
                UserDetails userDetails = userDetailsService.loadUserByUsername(email);
                if (jwtService.isTokenValid(jwt, userDetails)){
                    UsernamePasswordAuthenticationToken authenticationToken
                            = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities());
                    authenticationToken.setDetails(
                            new WebAuthenticationDetailsSource().buildDetails(request)
                    );
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
    

    to call the next filter in the chain:

            if (email != null && SecurityContextHolder.getContext().getAuthentication() == null){
                UserDetails userDetails = userDetailsService.loadUserByUsername(email);
                if (jwtService.isTokenValid(jwt, userDetails)){
                    UsernamePasswordAuthenticationToken authenticationToken
                            = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities());
                    authenticationToken.setDetails(
                            new WebAuthenticationDetailsSource().buildDetails(request)
                    );
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
            // causes the next filter in the chain to be invoked
            filterChain.doFilter(request, response);
    

    Here is the comment in the header of FilterChain for the doFilter method:

        /**
         * Causes the next filter in the chain to be invoked, or if the calling
         * filter is the last filter in the chain, causes the resource at the end of
         * the chain to be invoked.
         *
         * @param request
         *            the request to pass along the chain.
         * @param response
         *            the response to pass along the chain.
         *
         * @throws IOException if an I/O error occurs during the processing of the
         *                     request
         * @throws ServletException if the processing fails for any other reason
         */
        public void doFilter(ServletRequest request, ServletResponse response)
                throws IOException, ServletException;