I have created the Interceptor where i am trying to read the request body .But i keep getting the error
getInputStream() has been called for this request
How to solve it in postHandle ?I can do the same without any error by ovver riding preHandle but i need it in postHandle .
public class LoggerInterceptor implements HandlerInterceptor {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
try {
request.getReader();
String s = IOUtils.toString(request.getReader());
System.out.println(" post ---- " + s);//i want to log into DB
} catch (Exception e) {
System.out.println(" eror " + e);
}
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
Related
I am currently trying to figure out the best way to handle errors in a spring boot app.
Assume that the app consists of thymeleaf templates served on paths starting with /admin, and a REST API served on other URIs.
My project currently has the following configuration:
Thymeleaf error templates located at /src/main/resources/templates/error/{id}.html, defined for errors 400, 401, 403, 404, 500.
Default thymeleaf error template /src/main/resources/templates/error.html
HttpSecurity configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
(...)
http
.anonymous();
http
.exceptionHandling()
.authenticationEntryPoint((request, response, authException) -> response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()))
.accessDeniedHandler(new AccessDeniedHandlerImpl());
(...)
}
Exception Handlers for both rest and MVC:
#ControllerAdvice
public class ExceptionHandlers {
private final BasicErrorController basicErrorController;
public ExceptionHandlers(BasicErrorController basicErrorController) {
this.basicErrorController = basicErrorController;
}
#ExceptionHandler(Exception.class)
public Object handleAllExceptions(Exception e, HttpServletRequest request, HttpServletResponse response) {
return handle(e, request, response, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(EntityNotFoundException.class)
public Object handleEntityNotFoundException(EntityNotFoundException e, HttpServletRequest request, HttpServletResponse response) {
return handle(e, request, response, HttpStatus.NOT_FOUND, I18nCodes.ENTITY_NOT_FOUND);
}
/**
* We exclude all exceptions deriving from {#link AccessDeniedException} from custom exception handling.
*/
#ExceptionHandler(AccessDeniedException.class)
public Object handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request, HttpServletResponse response) {
throw e;
}
/**
* We exclude all exceptions deriving from {#link AppBaseException} from custom exception handling.
*/
#ExceptionHandler(AppBaseException.class)
public Object handleAppBaseException(AppBaseException e, HttpServletRequest request, HttpServletResponse response) {
throw e;
}
private Object handle(Exception e, HttpServletRequest request, HttpServletResponse response, HttpStatus status) {
return handle(e, request, response, HttpStatus.INTERNAL_SERVER_ERROR, I18nCodes.getCodeByStatus(status));
}
private Object handle(Exception e, HttpServletRequest request, HttpServletResponse response, HttpStatus status, String message) {
String header = request.getHeader("Accept");
if (header != null && header.contains("text/html")) {
setErrorCode(request, response, status);
return basicErrorController.errorHtml(request, response);
}
return createJsonResponse(message, status, request.getRequestURI());
}
private ResponseEntity<ErrorResponseDTO> createJsonResponse(String message, HttpStatus status, String path) {
ErrorResponseDTO errorResponseDTO = new ErrorResponseDTO()
.setTimestamp(new Timestamp(System.currentTimeMillis()))
.setStatus(status.value())
.setMessage(message)
.setPath(path)
.setError(status.name().toLowerCase());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return ResponseEntity.status(status).headers(httpHeaders).body(errorResponseDTO);
}
private void setErrorCode(HttpServletRequest request, HttpServletResponse response, HttpStatus httpStatus) {
request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, httpStatus.value());
response.setStatus(httpStatus.value());
}
}
Without the Exception handlers everything works about as expected, I get 401 error when I am not authenticated, and 403 when authenticated but lacking authorities. My custom exceptions are correctly mapped according to their message and Status defined in #ResponseStatus eg:
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class AccountInfoException extends AppBaseException {
protected AccountInfoException(String message) {
super(message);
}
protected AccountInfoException(String message, Throwable cause) {
super(message, cause);
}
public static AccountInfoException emailAlreadyExists() {
return new AccountInfoException(I18nCodes.EMAIL_EXIST);
}
public static AccountInfoException accountNotFound() {
return new AccountInfoException(I18nCodes.ACCOUNT_NOT_FOUND);
}
}
Will map to status 400 with one of two messages I18nCodes.EMAIL_EXIST or I18nCodes.ACCOUNT_NOT_FOUND.
I arrive on the correct thymeleaf error page depending on the status code - life is perfect.
The issue comes with the requirement of handling all other exceptions. It's not a possibility to let an unexpected error to the client. So for that I've defined an exception handler handing Exception, to return a generic 500 message. This breaks everything. I'm unable to access the Status codes of my custom exceptions
Authentication entry point and access denied handler are both ignored, and both cases of being not authenticated and having insufficient authority are handles in the same exception(AccessDeniedException). I also lose default mappings for some exceptions eg. org.springframework.security.authentication.LockedException returns status 401.
My temporary workaround was to handle all exceptions, and to create methods handling exceptions that I want to ignore and just rethrow the exception. I feel like the best thing I could do is remove all exception handlers and repack all container exceptions to my own custom exceptions, only issue is that there doesn't seem to be a way to do it.
I'm looking for a more permanent solution - what would be the best approach?
I use Java 11 und Spring.
Let's assume I have a method like this:
#POST
#Path(WebservicePaths.EXAMPLE_PATH)
public void processData(#NotNull #RequestBody TypeOfRequest request) {
try {
doSomething(request);
}
catch(Exception e) {
?????????????
}
}
At this moment the HTTP response is set automatically, right?
How can I get und print to the console body of the final response?
How can I change response status or other details to specific one only in block catch() {}?
You can inject HttpServletResponse and can call sendError method and pass the required HttpStatus in it. Below is the example:
#POST
#Path(WebservicePaths.EXAMPLE_PATH)
public void processData(#NotNull #RequestBody TypeOfRequest request, HttpServletResponse response) throws IOException {
try {
doSomething(request);
}
catch(Exception e) {
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
Here In the below simple code of RestTemplate's errorHandler I want to include the request body inside the log
I want to keep the code clean and not using the error handler out side restTemplate configuration class. So the only way i found is to handel it from rest template caller/user, WDYT?
#Configuration
public class HTTPConfiguration {
...
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR) {
LOGGER.error("Server error: {} {}", response.getStatusCode(), response.getStatusText());
} else if (response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR) {
LOGGER.error("Client error: {} {}", response.getStatusCode(), response.getStatusText());
}
}
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return (response.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR
|| response.getStatusCode().series() == HttpStatus.Series.SERVER_ERROR);
}
});
...
}
The straight answer is no, ResponseErrorHandler is an interface and only has three methods, the best you can get is request url and request method docs
handleError(ClientHttpResponse response)
Handle the error in the given response.
handleError(java.net.URI url, HttpMethod method, ClientHttpResponse response)
Alternative to handleError(ClientHttpResponse) with extra information providing access to the request URL and HTTP method.
handleError(java.net.URI url, HttpMethod method, ClientHttpResponse response)
Indicate whether the given response has any errors.
And ResponseErrorHandler has two implementation classes DefaultResponseErrorHandler, ExtractingResponseErrorHandler but none of them has method with HttpEntity as an argument DefaultResponseErrorHandler,and ExtractingResponseErrorHandler
I created a filter which authenticate each request header for JWT token:
public class JWTAuthenticationFilter extends GenericFilterBean {
private UserDetailsService customUserDetailsService;
private static Logger logger = LoggerFactory.getLogger(JWTAuthenticationFilter.class);
private final static UrlPathHelper urlPathHelper = new UrlPathHelper();
public JWTAuthenticationFilter(UserDetailsService customUserDetailsService) {
this.customUserDetailsService = customUserDetailsService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request, customUserDetailsService);
SecurityContextHolder.getContext().setAuthentication(authentication);
if (authentication == null) {
logger.debug("failed authentication while attempting to access " + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
}
filterChain.doFilter(request, response);
}
}
I want to throw a custom exception, and that exception returns a response:
#ResponseStatus(value=HttpStatus.SOMECODE, reason="There was an issue with the provided authentacion information") // 409
public class CustomAuthenticationException extends RuntimeException {
private static final long serialVersionUID = 6699623945573914987L;
}
How should I do this ? What is the best design to catch such exception thrown by filters ?
Is there any kind of exception handling mechanism provided by the Spring security that I can use and catch everythin in one point ?
Is there any other way to throw custom exceptions in a filter ?
Note: there is another question here which its accepted answer doesn't answer my question. I want to return a response before getting to any controller.
Error cases I want to handle:
1. Client sends an empty value for the Authorization header.
2. Client sends a malformed token
In both cases I get a response with 500 HTTP status code. I want to get 4XX code back.
Take a look at #ControllerAdvice
Here's an example from my project.
#ControllerAdvice
#RestController
public class GlobalExceptionHandler {
private final Logger log = Logger.getLogger(this.getClass().getSimpleName());
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(value = RuntimeException.class)
public Response handleBaseException(RuntimeException e) {
log.error("Error", e);
Error error = new Error(HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.name());
return Response.status(HttpStatus.BAD_REQUEST.value()).error(error, null).build();
}
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(value = NoHandlerFoundException.class)
public Response handleNoHandlerFoundException(Exception e) {
log.error("Error", e);
Error error = new Error(HttpStatus.NOT_FOUND.value(), HttpStatus.NOT_FOUND.name());
return Response.status(HttpStatus.NOT_FOUND.value()).error(error, null).build();
}
#ExceptionHandler(value = AuthenticationCredentialsNotFoundException.class)
public Response handleException(AuthenticationCredentialsNotFoundException e) {
log.error("Error", e);
Error error = new Error(ErrorCodes.INVALID_CREDENTIALS_CODE, ErrorCodes.INVALID_CREDENTIALS_MSG);
return Response.status(ErrorCodes.INVALID_CREDENTIALS_CODE).error(error, null).build();
}
#ResponseStatus(HttpStatus.UNAUTHORIZED)
#ExceptionHandler(value = UnauthorisedException.class)
public Response handleNotAuthorizedExceptionException(UnauthorisedException e) {
// log.error("Error", e);
return Response.unauthorized().build();
}
#ExceptionHandler(value = Exception.class)
public String handleException(Exception e) {
log.error("Error", e);
return e.getClass().getName() + " 14" + e.getMessage();
}
}
Edit
I believe you can response.sendError inside do Filter method.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
Authentication authentication = AuthenticationService.getAuthentication((HttpServletRequest) request, customUserDetailsService);
SecurityContextHolder.getContext().setAuthentication(authentication);
if (authentication == null) {
logger.debug("failed authentication while attempting to access " + urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid authentication.");
setUnauthorizedResponse(response);
return;
}
filterChain.doFilter(request, response);
}
public void setUnauthorizedResponse(HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
Response unAuthorizedResponse = Response.unauthorized().build();
try {
PrintWriter out = response.getWriter();
out.println(unAuthorizedResponse.toJsonString());
} catch (IOException e) {
log.error("Error", e);
}
}
I had the same issue with JWT tokens and posted the solution on this question, since the issue there was similar (he had trouble with filter exceptions)
Disclaimer: This is not the answer to the question asked, but this is a followup answer to the problem which Arian was asking.
As commented above, please see how you can autowire in places which are launched before spring container gives us access to beans.
Here I am autowiring my BlacklistJwtRepo
if (blacklistJwtRepo == null) { //Lazy Load because filter
ServletContext servletContext = req.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
blacklistJwtRepo = webApplicationContext.getBean(BlacklistJwtRepo.class);
}
This is where I am getting hold of the req object -
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
Final code looks like -
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
System.out.println("blacklistJwtRepo : " + blacklistJwtRepo);
//till here the autowired repo (blacklistJwtRepo) is null
if (blacklistJwtRepo == null) { //Lazy Load because filter
ServletContext servletContext = req.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
blacklistJwtRepo = webApplicationContext.getBean(BlacklistJwtRepo.class);
}
I have a Java Servlet class where I try to set the error status when there is an authentication issue in my application. However, the issue is, I am able to set the error status but am not able to set an error message in the body of the response. Below is my attempt:
AuthorizationFilter.java
public class AuthorizationFilter implements Filter {
#Autowired
private ExceptionHandler exceptionHandler;
#Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
UserSession userSession = null;
try {
userSession = authProvider.authenticate(request, response);
} catch(RestUsageException throwable) {
exceptionHandler.handle(throwable.getExceptionCode(), throwable.getMessage(), throwable, response);
response.sendError(throwable.getRespondStatus().value());
// response.sendError(throwable.getRespondStatus().value(), "Message");
return;
}
...more code here
}
ExceptionHandler.java
#Override
public void handle(String exceptionCode, String message, Throwable throwable, final ServletResponse response) throws IOException {
String uuid = UUID.randomUUID().toString();
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
String json = "{ \n" +
" \"errorCode\":\""+exceptionCode+"\",\n" +
" \"errorMessage\":\""+throwable.getMessage()+"\",\n" +
" \"success\":\"false\",\n" +
" \"errorId\":\""+uuid+"\"\n" +
"}";
response.getOutputStream().println(json);
// response.getOutputStream().flush();
}
My throwable contains the correct HTTP status that I want to display. I tried two things:
I tried to do follow this method: public void sendError(int sc,
java.lang.String msg)
throws java.io.IOException
But the message seems to only be displayed in the HTTP Status header.
I tried to response.getOutputStream().flush(); to flush out the response but then when I try to perform response.sendError(throwable.getRespondStatus().value()); afterwards, I get an error saying my Response is already committed, and my body shows up but the status ends up being 200.
Any ideas on how to set the error message body? Any help would be appreciated. Thanks!
You cant use the respons streams in combination with response.sendError(...).
Try to use respons.setStatus(...) instead.