I am using an ExceptionMapper in JAX-RS.
public class MyException implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
...
}
}
all works. However, is there anyway I can get a handle to the URL, or the HTTP request that generated the exception?
Thanks
Inject UriInfo using #Context:
#Context
private UriInfo uriInfo;
Then use the getRequestUri() method to get request URI:
URI uri = uriInfo.getRequestUri();
Refer to this answer for other types that can be injected with #Context.
Use #Context in class and use it to get original request:
#Context
private HttpServletRequest request;
#Override
public Response toResponse(ConstraintViolationException exception) {
String requestURI = request.getRequestURI();
Related
There is a rest api secured using keycloak(OAUTH) as below:
#PreAuthorize("hasRole('user')")
#GetMapping(value = "/employees", produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<List<Employee>> findAll(java.security.Principal principal,
#RequestParam(name = "filterationCriteria", required = false) String fields) {
if(Objects.nonNull(principal)){
return employeeManagementService.findAll(fields);
}
return null;
}
i want to consume this api using webclient as below:
public <T> T get(URI url, Class<T> responseType, Principal principal) {
return WebClient.builder().build().get().uri(url)
.header("Authorization", "Basic " + principal)
.retrieve().bodyToMono(responseType).block();
}
above method is throwing below exception from findAll() method, which generally happens if the principal is not found:
org.springframework.web.reactive.function.client.WebClientResponseException$Unauthorized:
401 Unauthorized from GET
Am i doing something wrong, is that the correct way to send principal with the request?
Any suggestion is appreciated.
Thanks in advance.
UPDATE
I am sending request to the secured rest-api using webclient from a different service and i need to pass the principal manually(may be including it into the header).
if i simply do below:
WebClient.builder().build().get().uri(url).retrieve().bodyToMono(responseType).block()
then the principal is coming null in the findAll method
Security config
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.anyRequest().permitAll().and()
.csrf().disable();
}
NOTE: i have put Authorize constraint at method level.
I have found a solution. the principal has to be sent like this:
public <T> T get(URI url, Class<T> responseType, Principal principal) {
RequestHeadersSpec uri = webClient.get().uri(url);
if (Objects.nonNull(principal)) {
uri = uri.header("Authorization", "Bearer "
+ ((KeycloakAuthenticationToken) principal).getAccount().getKeycloakSecurityContext().getTokenString());
}
ResponseSpec response = uri.retrieve();
Mono<T> bodyToMono = response.bodyToMono(responseType);
return bodyToMono.block();
}
I have an exception handler in Spring. The goal is for this exception handler to just add some additional headers to the response.
If I set the status to 404 using response.setStatus, the headers get overwritten it seems like and I do not see the "something_random" header on the client side. It works fine if I omit the setStatus(404), but then the client gets a 200 with the header. Is there a way to ensure that the 404 response has the custom headers set?
Spring version: 4.3.25
What I've tried:
#ExceptionHandler(CustomNotFoundException.class)
public void handleFailure(Exception ex, HttpServletResponse response) {
response.setHeader("something_random", "bob");
reseponse.setStatus(404);
}
Also tried (not sure if different):
#ExceptionHandler(CustomNotFoundException.class)
public ResponseEntity<Object> handleFailure(Exception ex, HttpServletResponse response) {
// Initialize 'headers'
return new ResponseEntity<>(headers, HttpStatus.NOT_FOUND);
}
I have tried the following with Spring Boot 2.4.2 (Spring Framework 5.3.3) and the custom header is present in the response:
#ExceptionHandler(InvalidResponseException.class)
public ResponseEntity<String> handleOpenApiResponseValidationExceptions(InvalidResponseException ex, HttpServletResponse response) {
return ResponseEntity.status(INTERNAL_SERVER_ERROR)
.header("custom", "value")
.body(ex.getMessage());
}
I Know this is old question but here is one way that can fit into this.
#EnableWebMvc
#Configuration
public class ExceptionHandlingConfig {
#Autowired
private DispatcherServlet dispatcherServlet;
#PostConstruct
private void configureDispatcherServlet() {
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
}
}
I have 3 REST services which are reading some common header parameters on the request. I need to use that parameters on my business services. instead of reading that common header parameters on each web service controller (#RestController), Is it possible to read that headers on request filter and make it available on the business services ? If yes, are there any examples to do this ?
You can get request object
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
and access the headers in business services using request object.
Like #Nitin suggest you can pass the request object from your controllers to your services and read the header there. There is no problem with that.
If you still want to read it in a filter and have it available in any #Service you can do as follows:
#Component
#Order(1)
public class HeaderReaderFilter implements Filter {
#Autowired
private HeaderDataHolder headerDataHolder;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
headerDataHolder.setHeaderContent(httpRequest.getHeader("header_field"));
chain.doFilter(request, response);
}
}
#RequestScope
#Component
public class HeaderDataHolder {
private String headerContent;
public String getHeaderContent() {
return headerContent;
}
public void setHeaderContent(String headerContent) {
this.headerContent = headerContent;
}
}
And then have the HeaderDataHolder #Autowired in your service classes. Notice the necessary #RequestScope so you have a different bean for each request.
For Oauth2.0, the access token should be passed either in header or in request parameter. But My requirement is to somehow extract this token from request body to support existing public apis which are passing access token as a JSON field in request body.
Using ContentCachingRequestWrapper, I am able to extract the access token field from request JSON in a custom filter. This custom filter is configured to be invoked before OAuth2AuthenticationProcessingFilter. But I am not able to set it either in the request header or add it to request parameter for the
OAuth2AuthenticationProcessingFilter to be able to extract this token and authenticate.
I referred to this link Spring Security: deserialize request body twice (oauth2 processing) which had a similar requirement. This talks about overriding HttpServletRequestWrapper.
With latest versions of Spring and Spring Boot, is there any other cleaner way of doing this rather than creating a HttpServletRequestWrapper?
Or is there any way of injecting my own implementation for BearerTokenExtractor (used by OAuth2AuthenticationProcessingFilter) which can also try to get the token from request attributes?
#Component
public class CustomOAuthTokenFilter extends GenericFilterBean {
#Autowired
private ObjectMapper objectMapper;
#Override
public void doFilter (ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
ContentCachingRequestWrapper reqWrapper = new ContentCachingRequestWrapper(httpRequest);
String requestBody = IOUtils.toString(reqWrapper.getInputStream(), java.nio.charset.StandardCharsets.UTF_8);
JsonNode requestJson = objectMapper.readTree(requestBody);
Iterator<JsonNode> jsonIter = requestJson.elements();
while (jsonIter.hasNext()){
JsonNode parent = jsonIter != null ? jsonIter.next() : null;
JsonNode hdr = parent != null ? parent.get("hdr") : null;
JsonNode accessTokenNode = hdr != null ? hdr.get("accessToken") : null;
String accessToken = accessTokenNode != null ? accessTokenNode.asText() : null;
}
chain.doFilter(reqWrapper, response);
}
}
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
private CustomOAuthTokenFilter customOAuthTokenFilter;
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/auth/token","/oauth/token", "/oauth/authorize**").permitAll().anyRequest()
.authenticated()
.and().addFilterBefore(customOAuthTokenFilter, AbstractPreAuthenticatedProcessingFilter.class);
}
}
I have multiple rest endpoints. userId (http header) is common in all endpoints. I want to apply a logic, let say set its default value if not provided or trim it if is provided in request before request enters the method (for eg: heartbeat). How can we achieve this in spring boot rest mvc.
#RestController
public class MyResource {
#RequestMapping(value = "/heartbeat", method= RequestMethod.GET)
public String heartbeat (#RequestHeader (value="userId", required=false) String userId)
{
...
}
}
Can you try this?
#Configuration
#Slf4j
public class HttpHeaderModificationConfig implements Filter {
private static final String HEADER_DEMO_NAME = "name";
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final HttpServletRequest req = (HttpServletRequest) request;
// modify HTTP Request Header
final HttpServletRequestWrapper reqWrapper = new HttpServletRequestWrapper(req) {
#Override
public String getHeader(String name) {
if (HEADER_DEMO_NAME.equals(name)) {
return "Changed";
}
return super.getHeader(name);
}
};
log.info("After Changed with Name {}", reqWrapper.getHeader(HEADER_DEMO_NAME));
chain.doFilter(reqWrapper, response);
}
}
One option i can think of using Filters i.e. create a Filter to check for the header userId and trim it if its present or provide it a default value if its not present and store it in another header lets say customUserId. In your controller you can comfortably use your custom headers to be sure of that it will be valid as per your requirements. You can refer to the below article for creating a generic bean
https://www.baeldung.com/spring-boot-add-filter