IllegalStateException : Cannot call sendError() after the response has been committed? - filter

#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String header = request.getHeader(HttpHeaders.AUTHORIZATION);
if (!StringUtils.hasText(header) || (StringUtils.hasText(header) && !header.startsWith("Bearer"))) {
chain.doFilter(request, response);
return;
}
final String token = header.split(" ")[1].trim();
UserDetails userDetails = userRepository.findByUsername(jwtUtil.getUsernameFromToken(token))
.orElse(null);
if (!jwtUtil.validateToken(token,userDetails)) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken
authentication = new UsernamePasswordAuthenticationToken(
userDetails, null,
userDetails == null ?
List.of() : userDetails.getAuthorities()
);
authentication.setDetails(
new WebAuthenticationDetailsSource().buildDetails(request)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
the error is here in doFilter
}
}

Related

automatically fill SecurityContext from Authorization header from Spring security authorization

I have this filter to read the token from the http header and set the security context :
public class AuthorizationFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null) {
jwtToken = requestTokenHeader.substring(7);
try {
username = tokenService.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
log.error("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
log.error("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
boolean isTokenValid = tokenService.validateToken(jwtToken, userDetails);
if (isTokenValid) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
Is there any default implementation that I can just config and use in spring boot security that reads the Authorization header and checks the token expiry date and then fill the needed info like user and roles in SecurityContext based on the token claims? since it seems like a very common functionality for user authorization , I thought maybe I didn't have to implement this part myself!

Getting data from security token in rest controller

I am working with Spring MVC and Spring Security for rest controllers, also I am using JWT. In some cases, I need to get a username from the token to provide it as a function parameter.
Now I am solving this problem by setting username as a request attribute in the security filter.
Can you advise me on a better way to do this?
Rest controller:
#GetMapping(path = "/user", produces = "application/json")
public String getUserFromToken(#RequestAttribute(name = "username") String username) throws JsonProcessingException {
List<Data> dataSet = userService.doSomeProcessing(username);
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(dataSet);
}
Security filter:
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
request.setAttribute("username", jwtTokenUtil.getUsernameFromToken(jwtToken));
}
}
chain.doFilter(request, response);
}
You should read about principal
https://www.baeldung.com/get-user-in-spring-security
There you will find a solution to this Spring Security problem, you yourself met with a similar one, thanks for the question and good luck

Catching CharConversionException in Spring Boot

I have a typical JWT authentication process on my app. I am able to catch most of the token errors, but when the user sends a bad bearer token (ex. ads.asd.d), CharConversionException is being thrown.
java.io.CharConversionException: Not an ISO 8859-1 character: [�]
The issue is that I can't catch it. I am getting this error.
exception java.io.CharConversionException is never thrown in body of corresponding try statement
Filter class:
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private MyUserDetailsService userDetailsService;
#Autowired
private JwtUtil jwtUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException, MalformedJwtException, CharConversionException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
try {
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
} catch (MalformedJwtException e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
} catch (CharConversionException e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
...
chain.doFilter(request, response);
}
Any idea on how I can catch that exception?

Spring security BasicAuthenticationFilter returns 403 instead of 401

I have implemented the JWT authentication and authorization. Everything is working fine, besides the unauthorized scenario
Unauthorized scenario: making a http call to a route without providing a authorization token.
Result: 403 forbidden instead of unauthorized
Here is my code:
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
After the
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
Executes, the response is 403
Here is my full class:
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// setting the user in the security context
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if(user != null){
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
Remark:
I had the same problem with UsernamePasswordAuthenticationFilter, and I solved it by overriding the default authenticationFailureHandler:
setAuthenticationFailureHandler(new JWTAuthenticationFailureHandler());
How can I get the correct 401 response code and body?
Thanks!
If you look at what BasicAuthenticationFilter which you are overriding with JWTAuthorizationFilter does when authentication fails, it calls authenticationEntryPoint.commence(request, response, failed) which sends 401
BasicAuthenticationEntryPoint
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.addHeader("WWW-Authenticate", "Basic realm=\"" + realmName + "\"");
response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
}
But you have behaviour that overridden and returning null. So instead of that try one of the following:
Throw BadCredentialsException where you are returning null
Do response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());

OncePerRequestFilter I can not return HttpServletResponse custom error

I have OncePerRequestFilter:
public class ApiAuthenticationFilter extends OncePerRequestFilter {
...
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = request.getHeader("X-Authorization");
if (StringUtils.isEmpty(token)) {
//return custom exception
return;
}
User user = userService.findUserByToken(token);
if (user == null) {
//return custom exception
return;
}
AuthUser authUser = new AuthUser(token, user);
SecurityContextHolder.getContext().setAuthentication(authUser);
filterChain.doFilter(request, response);
}
How can I return to client my custom exception?
I tried response.sendError(777, "Authorization token is invalid");
but client received error with status 500. I can send onli CONSTANT errors from HttpServletResponse loke this:
response.sendError(HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED, "Authorization token required");
But this HttpServletResponse not have all codes. For example I want send 666 error.

Resources