Reputation: 339
I am using Spring Boot with JWT and spring security in Project. Spring Boot : 2.3.5.RELEASE JJWT Verison : 0.11.2
I have created the JWT Authentication Filter class for interrupting requests.If the request contains JWT token in headers then parse the token, get roles, and set authentication object in the spring security context. My Authentication Filter class as follows
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import io.jsonwebtoken.Claims;
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String header = request.getHeader(JwtConstant.AUTHORIZATION);
if (StringUtils.isNotBlank(header) && header.startsWith(JwtConstant.BEARER)) {
String authToken = header.replace(JwtConstant.BEARER, "");
Claims claims = jwtTokenUtil.getJwtClaims(authToken);
String username = claims.getSubject();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,
"", getAuthoritiesFromString(claims));
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private Collection<? extends GrantedAuthority> getAuthoritiesFromString(Claims claims) {
return Arrays.stream(claims.get(JwtConstant.AUTHORITIES).toString().split(",")).map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
}
Token Parsing Method as follows:
public Claims getJwtClaims(String token) {
Claims claims = null;
try {
claims = Jwts.parserBuilder().setSigningKey(getPublicKey()).build().parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_EXPIRED),e, ErrorCode.TOKEN_EXPIRE);
} catch (SignatureException | MalformedJwtException e) {
throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_INVALID),e, ErrorCode.TOKEN_INVALID);
} catch (Exception e) {
throw new CustomException(env.getProperty(ExceptionMessage.TOKEN_PARSING),e, ErrorCode.INTERNAL_SERVER_ERROR);
}
return claims;
}
When parsing token if the oken is invalid or expired, then I am throwing Custom Exception which have Integer errorCode.
When request is containing expired or invalid token, first it goes to jwt authentication filter class. While parsing token I am getting following response :
{
"timestamp": "2020-11-30T08:28:40.319+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "Token Expired",
"path": "/api/users/profile"
}
As This exception is thrown from servlet filter It is not going to @RestControllerAdvise and its throwing inbuilt exception response. I want to throw my Custom Exception class with error code in this case. I tried out different solutions but not able to solve my problem. Is there any way to throw custom error code from this case.
I want below exception response,Which is my custom response class :
{
"message": "Token Expired",
"code": 101,
"error": true,
"timestamp": "2020-11-30T08:31:46.911+00:00"
}
Upvotes: 3
Views: 6989
Reputation: 659
Create a customerErrorController for "/error" like :
@RestController
public class CustomErrorController implements ErrorController {
private static final String PATH = "/error";
@RequestMapping(PATH)
public ResponseEntity<ErrorDetails> handleError(final HttpServletRequest request,
final HttpServletResponse response) throws Throwable {
throw (Throwable) request.getAttribute("javax.servlet.error.exception");
}
@Override
public String getErrorPath() {
return PATH;
}
}
And then @RestControllerAdvise will be able to pick exceptions.
Upvotes: 3