Search code examples
javaspring-bootspring-securityjwtbearer-token

Spring Boot with JWT : Full authentication is required to access this resource


I have this controller:

    @GetMapping("/daily")
    @PreAuthorize("hasRole('BASIC')")
    public ResponseEntity<TransitsResponse> dailyT(
            @RequestHeader(value = "Authorization") String authHeader)
            throws UnirestException, JsonProcessingException {
....
}

this class:

@Component
@Slf4j
public class AuthEntryPointJwt implements AuthenticationEntryPoint {

    //private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {

        if (!request.getServletPath().equalsIgnoreCase("/")) {
            log.error("Unauthorized error: {} with request {} from {}",
                    authException.getMessage(),
                    request.getServletPath(),
                    RequestUtils.getClientIp(request));
        }

        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

        final Map<String, Object> body = new HashMap<>();
        body.put("status", HttpServletResponse.SC_UNAUTHORIZED);
        body.put("error", "Unauthorized");
        body.put("message", authException.getMessage());
        body.put("path", request.getServletPath());

        final ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(response.getOutputStream(), body);

    }
}

and this:

@Slf4j
public class AuthTokenFilter extends OncePerRequestFilter {

  //@Autowired
  private final JwtUtils jwtUtils;

  //@Autowired
  private final UserDetailsServiceImpl userDetailsService;

    public AuthTokenFilter(JwtUtils jwtUtils, UserDetailsServiceImpl userDetailsService) {
        this.jwtUtils = jwtUtils;
        this.userDetailsService = userDetailsService;
    }

    //private static final Logger  = LoggerFactory.getLogger(AuthTokenFilter.class);

  @Override
  protected void doFilterInternal(@NotNull HttpServletRequest request,
                                  @NotNull HttpServletResponse response,
                                  @NotNull FilterChain filterChain)

      throws ServletException, IOException {

    try {

      log.info(String.valueOf(request));

      String jwt = parseJwt(request);

      log.info("jwt: " + jwt);

      if (jwt != null && jwtUtils.validateJwtToken(jwt)) {

        String username = jwtUtils.getUserNameFromJwtToken(jwt);

        log.info("username: " + username);

        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        log.info("userDetails: " + userDetails);

        UsernamePasswordAuthenticationToken authentication =
            new UsernamePasswordAuthenticationToken(
                userDetails,
                null,
                userDetails.getAuthorities());

        log.info("authentication: " + authentication);

        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

        log.info("authentication: " + authentication);

        SecurityContextHolder.getContext().setAuthentication(authentication);

        log.info("authentication: " + authentication);

      }

    } catch (Exception e) {
      log.error("Cannot set user authentication: {}", e);
    }

    filterChain.doFilter(request, response);
  }

  private String parseJwt (HttpServletRequest request) {

    String headerAuth = request.getHeader("Authorization");

    log.info("headerAuth: " + headerAuth);

    if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
      return headerAuth.substring(7);
    }

    return null;
  }

}

and

@Component
@Slf4j
public class JwtUtils {
    //private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);

    @Value("${bezkoder.app.jwtSecret}")
    private String jwtSecret;

    @Value("${bezkoder.app.jwtExpirationMs}")
    private int jwtExpirationMs;

    public String generateJwtToken(Authentication authentication) {

        log.info("generateJwtToken {} ", authentication);

        UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();

        log.info("userPrincipal {} ", userPrincipal);

        LocalDate nowPlus20Years = LocalDate.now().plusYears(20);

        log.info("nowPlus20Years {} ", nowPlus20Years);

        String jwt = Jwts.builder()
                .setSubject((userPrincipal.getUsername()))
                .setIssuedAt(new Date())
                //.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
                .setExpiration(Date.from(nowPlus20Years
                        .atStartOfDay(java.time.ZoneId.systemDefault()).toInstant()))
                .signWith(key(), SignatureAlgorithm.HS256)
                .compact();

        log.info("jwt {} ", jwt);

        return jwt;
    }

    private Key key() {
        return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtSecret));
    }

    public String getUserNameFromJwtToken(String token) {

        log.info("getUserNameFromJwtToken {} ", token);

        String str = Jwts.parserBuilder().setSigningKey(key()).build()
                .parseClaimsJws(token).getBody().getSubject();

        log.info("str {} ", str);

        return str;

    }

    public boolean validateJwtToken(String authToken) {
        try {
            log.info("validateJwtToken {} ", authToken);
            Jwts.parserBuilder().setSigningKey(key()).build().parse(authToken);
            log.info("JWT token is valid.");
            return true;
        } catch (MalformedJwtException e) {
            log.error("Invalid JWT token: {}", e.getMessage());
        } catch (ExpiredJwtException e) {
            //FIXME
            log.error("JWT token is expired: {}", e.getMessage());
            return true;
        } catch (UnsupportedJwtException e) {
            log.error("JWT token is unsupported: {}", e.getMessage());
        } catch (IllegalArgumentException e) {
            log.error("JWT claims string is empty: {}", e.getMessage());
        }

        return false;
    }
}

in my localhost is working fine, but in another server I can't connect. I see this on the logs:

2025-02-09 16:00:37.583 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(49) - org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@333422c4
2025-02-09 16:00:37.585 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@parseJwt(94) - headerAuth: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxscG9pbnRlciIsImlhdCI6MTczOTEwODUyMiwiZXhwIjoyMzcwMjExMjAwfQ.lLmZum2iWox4T5lt7KYxZTXorm-VQjB8HVsw8BCdt56
2025-02-09 16:00:37.586 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(53) - jwt: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxscG9pbnRlciIsImlhdCI6MTczOTEwODUyMiwiZXhwIjoyMzcwMjExMjAwfQ.lLmZum2iWox4T5lt7KYxZTXorm-VQjB8HVsw8BCdt56
2025-02-09 16:00:37.587 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.JwtUtils@validateJwtToken(74) - validateJwtToken eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxscG9pbnRlciIsImlhdCI6MTczOTEwODUyMiwiZXhwIjoyMzcwMjExMjAwfQ.lLmZum2iWox4T5lt7KYxZTXorm-VQjB8HVsw8BCdt56 
2025-02-09 16:00:37.591 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.JwtUtils@validateJwtToken(76) - JWT token is valid.
2025-02-09 16:00:37.593 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.JwtUtils@getUserNameFromJwtToken(61) - getUserNameFromJwtToken eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJudWxscG9pbnRlciIsImlhdCI6MTczOTEwODUyMiwiZXhwIjoyMzcwMjExMjAwfQ.lLmZum2iWox4T5lt7KYxZTXorm-VQjB8HVsw8BCdt56 
2025-02-09 16:00:37.600 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.JwtUtils@getUserNameFromJwtToken(66) - str nullpointer 
2025-02-09 16:00:37.601 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(59) - username: nullpointer
2025-02-09 16:00:37.607 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(63) - userDetails: com.taradell.security.services.UserDetailsImpl@56736d20
2025-02-09 16:00:37.607 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(71) - authentication: UsernamePasswordAuthenticationToken [Principal=com.taradell.security.services.UserDetailsImpl@56736d20, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_SUBSCRIBED]]
2025-02-09 16:00:37.608 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(75) - authentication: UsernamePasswordAuthenticationToken [Principal=com.taradell.security.services.UserDetailsImpl@56736d20, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=83.44.48.136, SessionId=null], Granted Authorities=[ROLE_SUBSCRIBED]]
2025-02-09 16:00:37.612 [http-nio-8081-exec-3] INFO  [] c.m.security.jwt.AuthTokenFilter@doFilterInternal(79) - authentication: UsernamePasswordAuthenticationToken [Principal=com.taradell.security.services.UserDetailsImpl@56736d20, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=83.44.48.136, SessionId=null], Granted Authorities=[ROLE_SUBSCRIBED]]
2025-02-09 16:00:37.633 [http-nio-8081-exec-3] ERROR [] c.m.security.jwt.AuthEntryPointJwt@commence(29) - Unauthorized error: Full authentication is required to access this resource with request /error from 83.44.48.136

Solution

  • The error indicates that path /error lacks .permitAll() in SecurityFilterChain. Hence, add this to your SecurityFilterChain:

    .requestMatchers("/error").permitAll().
    

    OP confirms that this solved the issue.