I am currently implementing Spring Security OAuth2 and would like to provide my own custom error page when an OAuth2Exception is thrown. Currently there is a Framework endpoint in WhitelabelErrorEndpoint.java that is mapped to /oauth/error, which provides the default error handling. The reference docs claim:
All you need to do is provide a Spring MVC controller with #RequestMappings for those endpoints, and the framework defaults will take a lower priority in the dispatcher.
However, in practice I am not seeing my Spring controller taking priority over the Whitelabel default. What am I doing wrong?
Example Controller:
#Controller
#Scope(WebApplicationContext.SCOPE_REQUEST)
public class OAuthExceptionController {
private static final Logger logger = LoggerFactory.getLogger(OAuthExceptionController.class);
#RequestMapping("/oauth/error")
public ModelAndView defaultOAuthErrorHandler(HttpServletRequest request) throws Exception {
logger.error("Oauth2 exception occurred");
Map<String, Object> model = Maps.newLinkedHashMap();
model.put("path", request.getServletPath());
return new ModelAndView("error/accessDenied", model);
}
}
Related
In my Spring Boot 2.2.x web application I need to intercept 404 errors so I can execute some custom logic before the error page is rendered or the json with error info is returned.
What's the correct way to achieve this?
At the moment I'm trying with a #ControllerAdvice class, but I don't know which exception to handle with the #ExceptionHandler method (if any is thrown)...
NOTE: I have already tried to intercept NoHandlerFoundException, but this doesn't seem to work... (the handler is not triggered).
#ControllerAdvice
public class ErrorHandler {
#ExceptionHandler(NoHandlerFoundException.class)
public void handleException() {
System.out.println("It's a 404!");
}
}
I also enabled this in my application.properties:
spring.mvc.throw-exception-if-no-handler-found=true
Any other suggestion?
I finally found the solution (or part of it) in this question: Custom exception handler for NoHandlerFoundException is not working without #EnableWebMvc
By default Spring doesn't throw an exception for 404 errors. You need to enable this property in application.properties (or yaml):
spring.mvc.throw-exception-if-no-handler-found=true
But you also need to set this property:
spring.mvc.static-path-pattern: /static
This restriction prevents Spring to search for static contents matching the missing path/handler.
Now you can intercept a NoHandlerFoundException as usual with #ExceptionHandler.
NOTE Before setting the spring.mvc.static-path-pattern property I tried to add #EnableWebMvc to a configuration class in my project: this way the NoHandlerFoundException was thrown but I obviously lost all the Spring Boot autoconfigurations.
Use NoHandlerFoundException. This maps to 404 in spring boot.
In the same class you use #ControllerAdvice implement ErrorController.
#ControllerAdvice
public class ControllerAdvice implements ErrorController {
#RequestMapping("/error")
public ResponseEntity<Map> handleError(HttpServletResponse response) {
Map errorMapJson = new HashMap();
if(response.getStatus() == HttpStatus.NOT_FOUND.value()) {
errorMapJson.put("type", "404 Error");
}
return new ResponseEntity<Map>(errorMapJson, HttpStatus.NOT_FOUND);
}
#Override
public String getErrorPath() {
return "/error";
}
}
Is there a way to use spring-boot Restful without "HttpServletRequest" to retrieve client's request and others ?
Like, to intercept a calling to server, has to do a scan, how to do it without Servlet API support?
It is a little strange but really donot know if there is a calling convertion that has already implemented this function. Can it be done by following annoation definitions?
#RequestMapping
consumes: It defines an array of consumable media types of mapped
request.
produces: It defines an array of producible media types of mapped request.
headers: It defines the acceptable headers of mapped request.
params: It defines the parameters of the mapped request, narrowing the primary mapping.
path: It defines path mapping URIs in servlet environment.
name: It assigns a name to this mapping.
value: It defines primary mapping expressed by this annotation.
Thanks :)
If You use Spring boot release between 1.2 - 2.0
You may use Spring Boot Actuator trace endpoint.
You can see last 100 incoming request with trace endpoint.
Add this dependency in your pom file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Add this configurations in application.properties.
endpoints.actuator.enabled=true
endpoints.trace.enabled=true
endpoints.trace.sensitive=false
management.trace.include=request-headers,response-headers,cookies,errors,parameters
then make request http://yourhost:yourport/trace (etc. http://localhost:8080/trace)
see the logs.
If you want to save logs,
you can create your custom trace repository :
#Component
public class customTrace implements TraceRepository {
private final TraceRepository lastOneHundretLogs = new InMemoryTraceRepository();
#Override
public List<Trace> findAll() {
return null;
}
#Override
public void add(Map<String, Object> map) {
}
}
logs is in the lastOneHundretLogs object.you can take it.
As an alternative ,you can log requests with logger
#Component
public class InMemoryTraceRepository implements TraceRepository {
private static final Logger HTTP_LOGGER = LoggerFactory.getLogger("http-logger");
// For security matters it's better to not expose Traces on HTTP
#Override
public List<Trace> findAll() {
return null;
}
#Override
public void add(Map<String, Object> map) {
Trace trace = new Trace(new Date(), map);
String traceInfo = trace.getInfo().toString();
HTTP_LOGGER.info(traceInfo);
}
}
Through the documentation of spring framework, I got to know about customizing exception handling mechanism of spring application. As per the documentation, It is stated that spring provides some default exception Resolvers.
SimpleMappingExceptionResolver
A mapping between exception class names and error view names. Useful for rendering error pages in a browser application.
DefaultHandlerExceptionResolver
Resolves exceptions raised by Spring MVC and maps them to HTTP status codes. See also alternative ResponseEntityExceptionHandler and REST API exceptions.
ResponseStatusExceptionResolver
Resolves exceptions with the #ResponseStatus annotation and maps them to HTTP status codes based on the value in the annotation.
ExceptionHandlerExceptionResolver
Resolves exceptions by invoking an #ExceptionHandler method in a #Controller or a #ControllerAdvice class. See #ExceptionHandler methods.
But in my requirement, I do not want the support of all these resolvers I just want ExceptionHandlerExceptionResolver to handle the exception in my Spring Application. So for that, I have added the following Configuration in my DispatcherServlet.
DispatcherServlet related configuration.
public void onStartup(ServletContext servletCxt) throws { ServletException {
......
dServlet.setDetectAllHandlerExceptionResolvers(false);
dServlet.setThrowExceptionIfNoHandlerFound(false);
.....
}
And in my Bean configuration java class, I have created following bean.
#Bean(name=DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME)
HandlerExceptionResolver customExceptionResolver ( ) {
return new ExceptionHandlerExceptionResolver();
}
In the documentation, it is stated that
/**
* Set whether to detect all HandlerExceptionResolver beans in this servlet's context. Otherwise,
* just a single bean with name "handlerExceptionResolver" will be expected.
* Default is "true". Turn this off if you want this servlet to use a single
* HandlerExceptionResolver, despite multiple HandlerExceptionResolver beans being defined in the context.
*/
But even after all such configuration, I see HTML error pages and not JSON error response.
Sample ExceptionHandler method.
#ExceptionHandler(value={NullPointerException.class})
public ResponseEntity<Object> nullPointerExceptionHandler(NullPointerException e){
logger.info("local exception resolver running");
Map<String,Object> map=new HashMap<String,Object>(6);
map.put("isSuccess",false);
map.put("data",null);
map.put("status",HttpStatus.INTERNAL_SERVER_ERROR);
map.put("message", e.getMessage());
map.put("timestamp",new Date());
map.put("fielderror",null);
return new ResponseEntity<Object>(map,HttpStatus.BAD_GATEWAY);
}
What I am doing wrong? I only need the support of ExceptionHandlerExceptionResolver throughout my spring application to maintain consitency.
I am quite new to Spring, so sorry if my question sounds silly.
I am trying to add basic HTTP Auth to my Spring based REST API.
I have been following quite a few tutorials and reference documentations for now, but they all seem to indicate the same thing so it seems obvious I must be missing something.
What I want is to define a simple configuration where every request is behind HTTP Auth by default, but then we can define method level security as needed using #PreAuthorize, or by modifying the HttpSecurity configuration.
I have defined a very simple configuration as such:
#Configuration
#EnableWebMvc
#EnableWebSecurity
#ComponentScan(basePackages = "com.app.rest")
public class RESTConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().hasAnyRole("USER")
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
}
I then have a very simple controller (no service, nothing fancy).
Here is a very basic overview:
#RestController
#RequestMapping("/images")
public class ImagesController {
#JsonView(View.Basic.class)
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ObjectXvo getImageByID(#PathVariable String id) throws IOException {
ObjectXvo result = doThings();
return result;
}
}
The only noticeable difference compared to all the online tutorials is the way we load this API. Because we already have a Jetty container running other things, we have decided to reuse it instead of using a WebAppInitializer like most do online.
Here is an extract of how the REST API is defined:
// Creating REST Servlet
ServletContextHandler restHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
restHandler.setErrorHandler(null);
restHandler.setContextPath("/rest");
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("com.app.test");
WebApplicationContext webAppContext = context;
DispatcherServlet dispatcherServlet = new DispatcherServlet(webAppContext);
ServletHolder springServletHolder = new ServletHolder("REST dispatcher", dispatcherServlet);
restHandler.addServlet(springServletHolder, "/");
restHandler.addEventListener(new ContextLoaderListener(webAppContext));
// Creating handler collection
ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
handlerCollection.addHandler(anotherHandler);
handlerCollection.addHandler(restHandler);
handlerCollection.addHandler(yetAnotherHandler);
// Setting server context
server.setHandler(handlerCollection);
The thing is, when running the app, is that I can still access all URLs like if I had not setup any security scheme.
When debugging the application, I can see that the RESTConfiguration is being processed as breakpoints inside my configure methods are definitely being hit.
We are quite set on avoiding the usage of XML files as long as we can, as we think annotations are better so far.
Can anyone point me in the right direction as to why this security configuration is not being activated?
EDIT:
Not sure how relevant that is, but if I try to add a #PreAuthorize to a REST method, I get the following error:
HTTP ERROR: 500
Problem accessing /rest/images/I147. Reason:
An Authentication object was not found in the SecurityContext
There is plenty of info on the internet on how to fix this, but I am wondering if this is not related.
You have to register springSecurityFilterChain, see Spring Security Reference:
The next step is to register the springSecurityFilterChain with the war. This can be done in Java Configuration with Spring’s WebApplicationInitializer support in a Servlet 3.0+ environment. Not suprisingly, Spring Security provides a base class AbstractSecurityWebApplicationInitializer that will ensure the springSecurityFilterChain gets registered for you. The way in which we use AbstractSecurityWebApplicationInitializer differs depending on if we are already using Spring or if Spring Security is the only Spring component in our application.
If you don't use AbstractSecurityWebApplicationInitializer you have to register springSecurityFilterChain manually:
webAppContext.getServletContext()
.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC), false, "/*");
See also:
ServletContext#addFilter
FilterRegistration#addMappingForUrlPatterns
I have a question about data binding in Spring MVC.
I have a Controller which accepts a JSON request in the form of #RequestBody. I have all the JSR 303 validations in place and it works like a charm.
JSON Request
public class TestJSONRequest {
#Size(min=10,message="{invalid.demo.size}")
String demo;
int code;
}
Controller
#Controller
#RequestMapping("/test")
public class TestController {
public void testEntry(#RequestBody TestJSONRequest jsonRequest,ModelMap map)
Set<ConstraintViolation<TestJSONRequest>> violationList = validator.val(jsonRequest);
....
....
TestJSONResponse response = // Do complex Logic.
modelMap.addattribute("TestJSONResponse",response);
}
}
But JSR 303 validations kick in once the incoming JSON data is bound to the Request object.
If I send ab in the code field of the input JSON request, binding would itself fail.
How do I handle that?
I want to catch those data binding errors and do some kind of generalized error handling in my controller.
Could you please help me out on this?
P.S - I am using Spring 3.0.3
According to the current Spring documentation (V3.1) :
Unlike #ModelAttribute parameters, for which a BindingResult can be used to examine the errors, #RequestBody validation errors always result in a MethodArgumentNotValidException being raised. The exception is handled in the DefaultHandlerExceptionResolver, which sends a 400 error back to the client.
Now you can to tell Spring that you'd like to handle this, by creating a new method, as follows:
#ExceptionHandler(MethodArgumentNotValidException.class)
public String handleValidation(MethodArgumentNotValidException e, ModelMap map) {
List<ObjectError> errors = e.getBindingResult() .getAllErrors();
// your code here...
return "path/to/your/view";
}
Finally, have a read of the Spring docs wrt #ExceptionHandler. There's most likely some useful information there.