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
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.