I'm using CommonsMultipartResolver for multipart files. I have a bean:
#Bean
public CommonsMultipartResolver commonsMultipartResolver() {
final CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
commonsMultipartResolver.setMaxUploadSize(maxUploadSize);
return commonsMultipartResolver;
}
And then a controller with method:
#RestController
public class Ctrl {
#PostMapping
public ResponseEntity upload(MultipartFile multipartFile) throws MaxUploadSizeExceededException {
...
}
}
And exception handler:
#RestControllerAdvice(annotations = RestController.class)
public class RestControllerExceptionHandler {
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ExceptionHandler({
MaxUploadSizeExceededException.class
})
public ResponseEntity handleBadRequest(final Exception exception) {
...
}
}
Also I've configured MultipartFilter
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
And added Tomcat configuration to conf/server.xml file maxSwallowSize="-1"
But when I'm trying to upload file > max size I'm just getting screen with exception.
HTTP Status 500 - Maximum upload size of 1 bytes exceeded; nested exception is org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (172065) exceeds the configured maximum (1)
Am I missing something with the configuration ? I now that exception is thrown on tomcat level and maybe it just not possible to catch it inside Spring controller advice? I found few links with similar configuration and it says that it's possible.
Related
I am having issue using #PreAuthorize("hasRole('ADMIN')") annotation. My controller code is as below which has method welcome() which can only be accessed by user having role ADMIN:
#CrossOrigin(origins = "*")
#RestController
#RequestMapping("/user/auth")
public class TestController {
#GetMapping("/welcome")
#PreAuthorize("hasRole('ADMIN')")
public String welcome() {
return "Welcome!!!";
}
}
Below is my security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/user/auth/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
AuthEntryPointJwt class is as below:
#Component
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, ServletException {
logger.error("Unauthorized error: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
}
}
This is the url I am using: http://localhost:8080/user/auth/welcome and this is response
{
"message": null,
"httpStatusCode": 404,
"errorLevelCode": "0x2",
"errorMessage": "Access is denied",
"apiPath": null,
"httpMethod": null
}
So, I am sending jwt Bearer + token in Authorization header using postman and it is throwing 404. It should have returned resource with token after sending authorization header. I am not able to figure out what the problem is. It would be great to have some suggestion or to know something that I am doing wrong over here. Thanks in advance.
Error 404 suggest that you have problem with endpoint mapping (no resource found in path). If it was security issue you would get error 401 (unauthorized) or error 403 (forbidden).
If I am right then you should get same error when you remove #PreAuthorize and add "welcome" path to security config with access "permitAll".
When a Spring bean is annotated with SCOPE_REQUEST, it is created and destroyed every time a HTTP request is received by the servlet. If this bean creation fails, a server error is sent back to the caller.
In this trivial example, the creation of the MyInputs bean is dependent on the contents of the HTTP request.
#Configuration
class ApplicationConfiguration {
#Bean
#Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public MyInputs myInputs(HttpServletRequest request) {
String header1 = request.getHeader("header1");
if (header1 == null) {
throw new MyException("header1 is missing");
}
return new MyInputs(header1);
}
}
If the HTTP request does not contain a required header, a BeanCreationException will be thrown. This is translated into an unhelpful "500 Internal Server Error" response.
I would like to return a more user-friendly response code and body, for example, a "400 Bad Request" with a helpful message. How do I customize this response translation? I cannot find any lifecycle hooks which will allow this.
Note: This is how the request-scoped bean is consumed:
#RestController
public class MyController {
private final Provider<MyInputs> myInputsProvider;
#Autowired
public MyController(Provider<MyInputs> myInputsProvider) {
this.myInputsProvider = myInputsProvider;
}
#GetMapping("/do-stuff")
public void doStuff() {
// Get the inputs for the current request
MyInputs myInputs = myInputsProvider.get();
// ...
}
}
You can use #ControllerAdvice annotation in order to handle exceptions after are thrown.
Also you need to use #ExceptionHandler in order to handle the exception.
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(MyException.class)
public final ResponseEntity<CustomError> handleException(MyException ex, WebRequest request) {
CustomError error = new CustomError();
error.setMessage(ex.getMessage());
error.setStatus(HttpStatus.BAD_REQUEST);
return new ResponseEntity<>(error, null, HttpStatus.BAD_REQUEST);
}
}
I have the following code base:
#Component
public class DummyRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
rest("/upload").post().to("file://rest_files");
}
#Bean
public ServletRegistrationBean servletRegistrationBean() {
SpringServerServlet serverServlet = new SpringServerServlet();
ServletRegistrationBean regBean = new ServletRegistrationBean( serverServlet, "/rest/*");
Map<String,String> params = new HashMap<>();
params.put("org.restlet.component", "restletComponent");
regBean.setInitParameters(params);
return regBean;
}
#Bean
public org.restlet.Component restletComponent() {
return new org.restlet.Component();
}
#Bean
public RestletComponent restletComponentService() {
return new RestletComponent(restletComponent());
}
}
I upload file using postman:
It is actually usual csv.
But when I open file my application stored - I see file with following content:
Obvious that file contains full request information.
How can I save only file without other data from http request?
P.S.
I tried to register callback:
#Override
public void process(Exchange exchange) throws Exception {
System.out.println(exchange);
final MultipartFile mandatoryBody = exchange.getIn().getBody(MultipartFile.class);
but it returns null
I want to handle 404 page not found exception in my Spring MVC web app, I'm using SPRING 4.2.5.RELEASE, I had read several question regarding this topic but the similar questions are using a different spring java configuration.
I have a Global Exception Handler Controller class that have all my Exceptions, this class works fine but I can't handle a 404 page not found exception.
This is the approach that I take following a tutorial
1) I created a class named ResourceNotFoundException that extends from RuntimeException and I putted this annotation over the class definition #ResponseStatus(HttpStatus.NOT_FOUND)
like this:
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
}
2) I created this method in my exception's controller class
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handleResourceNotFoundException() {
return "notFoundJSPPage";
}
But still when I put a URL that doesn't exist I get this error "No mapping found for HTTP request with URI"
The questions that I had read said that I need to enable to true an option for the Dispatcher but since my configuration it's different from the other questions and I don't have a Web.xml I couldn't apply that.
Here it's my Config.java
#EnableWebMvc
#Configuration
#ComponentScan({"config", "controllers"})
public class ConfigMVC extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/WEB-INF/resources/");
}
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
}
Here is my WebInitializer
public class WebInicializar implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ConfigMVC.class);
ctx.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
Here is my Global Exception Handler Controller
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(value = NullPointerException.class)
public String handleNullPointerException(Exception e) {
System.out.println("A null pointer exception ocurred " + e);
return "nullpointerExceptionPage";
}
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public String handleAllException(Exception e) {
System.out.println("A unknow Exception Ocurred: " + e);
return "unknowExceptionPage";
}
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handleResourceNotFoundException() {
return "notFoundJSPPage";
}
}
And the class I created that extends Runtime Exception
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
}
I solved the problem by putting this line in my onStartup method in the WebApplicationInitializer.class
this it's the line I add servlet.setInitParameter("throwExceptionIfNoHandlerFound", "true");
this is how it looks the complete method with the new line I added
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ConfigMVC.class);
ctx.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
Then I created this controller method in my GlobalExceptionHandlerController.class
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handle(NoHandlerFoundException ex) {
return "my404Page";
}
and that solved my problem I deleted the handleResourceNotFoundException controller method in my GlobalExceptionHandlerController.class since it wasn't necessary and also I deleted the exception class ResourceNotFoundException.class that I created
You can also extend AbstractAnnotationConfigDispatcherServletInitializer and override this method:
#Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
final DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
OR this one:
#Override
public void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
And finally in your ControlerAdvice use this:
#ExceptionHandler(NoHandlerFoundException.class)
public String error404(Exception ex) {
return new ModelAndView("404");
}
Add following code in any controller and create a 404 page
#GetMapping("/*")
public String handle() {
return "404";
}
I found that the answer by zygimantus didnt work for some reason, so if you also have the same problem , then instead of declaring an "#ExceptionHandler", add one of these to a "#Configuration" class instead. I put mine in my WebMvcConfigurerAdapter
#Bean
public HandlerExceptionResolver handlerExceptionResolver(){
HandlerExceptionResolver myResolver = new HandlerExceptionResolver(){
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {
//return your 404 page
ModelAndView mav = new ModelAndView("404page");
mav.addObject("error", exception);
return mav;
}
};
return myResolver;
}
But make sure you also follow the rest of zygimantus ie
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
In my Rest API created in Spring Boot I am trying to indicate that resource has not been found to throw an exception and do exception handling with #ControllerAdvice:
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(NotFoundException.class)
public void notFound() {
// do something...
}
}
my exception class:
#ResponseStatus(HttpStatus.NOT_FOUND)
public final class NotFoundException extends RuntimeException {
public NotFoundException() {
}
public NotFoundException(String message) {
super(message);
}
}
and testing method:
#RequestMapping(value = "/no", method = RequestMethod.GET)
public void notExists() {
throw new NotFoundException();
}
but instead HTTP 404 is throw HTTP 500 and GlobalControllerExceptionHandler is not activated.
Update 1:
From catalina.out:
2015-09-12 22:42:59.510 ERROR 71872 --- [o-8080-exec-140]
o.s.boot.context.web.ErrorPageFilter : Cannot forward to error
page for request [/persons/no/] as the response has already been
committed. As a result, the response may have the wrong status code.
If your application is running on WebSphere Application Server you may
be able to resolve this problem by setting
com.ibm.ws.webcontainer.invokeFlushAfterService to false
I found this answer:
#Bean
public ErrorPageFilter errorPageFilter() {
return new ErrorPageFilter();
}
#Bean
public FilterRegistrationBean disableSpringBootErrorFilter(ErrorPageFilter filter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(filter);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}