Hi folks,
maybe I've missed something, but the current implementation of BasicErrorController
(method errorHtml
) do not prevent the user from calling /error
directly in his browser. This allows the user to see the rendered error page in an unintended context.
Would it be better to add a pre-check and redirect the user to the application's main entry point (which needs to be defined somewhere) if
request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)
returns null
?
Best regards, Sascha
Comment From: dsyer
I don't see much benefit in that, and it's actually pretty useful to be able to see the error page if you want to tweak it.
Comment From: saschaszott
Thanks! Is it possible to overwrite the /error
request mapping and implement the requested behavior in a custom controller action?
Comment From: dsyer
Yes. See docs here: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-error-handling. You can also disable the default error handling by excluding it in @EnableAutoConfiguration
.
Comment From: saschaszott
Hi, thanks for the pointer to the current-SNAPSHOT version of the Spring Boot documentation (there was a crucial update in the section on Error Handling recently)!
My fix was to implement a CustomErrorController
class that implements org.springframework.boot.actuate.web.ErrorController
(extending BasicErrorController
was not successful):
@Controller
public class CustomErrorController implements ErrorController {
private BasicErrorController basicErrorController = new BasicErrorController();
@Value("${error.path:/error}")
private String errorPath;
@Override
public String getErrorPath() {
return errorPath;
}
@Override
public Map<String, Object> extract(RequestAttributes attributes, boolean trace, boolean log) {
return basicErrorController.extract(attributes, trace, log);
}
@RequestMapping(value = "${error.path:/error}", produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request) {
if (request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) == null) {
// do not allow the user to call this action directly
return new ModelAndView("redirect:");
}
return basicErrorController.errorHtml(request);
}
@RequestMapping(value = "${error.path:/error}")
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
return basicErrorController.error(request);
}
}
If there is a more elegant solution, so please let me know. I'm using Spring Boot 1.0.2.RELEASE.
Comment From: saschaszott
Please note, that this approach does not work with the latest release of Spring Boot (1.1.4).
Comment From: dsyer
Sorry, which approach? What doesn't work?
Comment From: saschaszott
The "fix" to define a custom ErrorController class that has an instance of type BasicErrorController
. Since Spring Boot 1.1.x the BasicErrorController
's constructor expects an object that implements the ErrorAttributes
interface.
If there is a (elegant) way to disallow the user to call /error
directly then please let me know.
Comment From: dsyer
You can still create a BasicErrorController
subclass and inject the ErrorAttributes
. Or you can create an ErrorAttributes
subclass if you don't want the default one. I don't see the problem.
Comment From: saschaszott
Thank you, Dave! I wasn't aware of the fact that it's possible to extend a Spring Boot controller class and overwrite an existing request mapping.
With Spring Boot 1.1.4 the following is a valid approach to prevent the user from calling /error
directly:
@Controller
public class CustomErrorController extends BasicErrorController {
public CustomErrorController() {
super(new DefaultErrorAttributes());
}
@RequestMapping(value = "${error.path:/error}", produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request) {
if (request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE) == null) {
// do not allow the user to call this action directly
return new ModelAndView("redirect:");
}
return super.errorHtml(request);
}
}
Comment From: rafis
Does it the same in Spring Boot 2.x? When you extend BasicErrorController the main problem is with the two constructors. Spring requires no argument constructor and I don't know what to pass in. Seems like I can pass new DefaultErrorAttributes()
, but what should I pass in for ErrorProperties?