Update request headers before goind to controller - java

I can't update a header inside my interceptor before it gets to my controller, through the interceptor I would like to modify an already present header
public class MyInterceptor implements Filter {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse res = (HttpServletResponse) servletResponse;
//something
//myHeaders is still present when send a request
req.setAttribute("myHeaders","someValue");
chain.doFilter(req, rest);
}
}
So that inside the controller I can get the modified header:
#RestController
#RequestMapping("/")
public class FooClass{
#Autowired
private Service service;
#GetMapping("/foo")
public ResponseEntity<Void> fooApi(
#RequestHeader(value = "myHeaders") String myHeaders,
) {
service.doSomething(myHeaders);
return ResponseEntity.ok().build();
}
}
How could I do? I tried to do some research but failed.

In your Filter you can create an anonymous subclass of HttpServletRequestWrapper, override the method public String getHeader(String name) so that it returns a specific value for the header name you care about (and delegates to super.getHeader(String) if it's not the header name you care about).
Something like this:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest servletRequest = (HttpServletRequest) request;
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(servletRequest) {
#Override
public String getHeader(String name) {
if ("myHeader".equalsIgnoreCase(name)) {
return "Some value";
}
return super.getHeader(name);
}
};
chain.doFilter(requestWrapper, response);
}

Related

Spring authentication filter throwing wrong error message

I have a custom filter that is used to authenticate the user. I am always getting full authentication requried error even though I have thrown a custom exception with specific message & added exception handler as well.
Code for filter:
#Slf4j
#Component
public classTokenValidationFilter extends OncePerRequestFilter {
#Autowired
private TokenValidationHelper tokenValidationHelper;
#Override
protected void doFilterInternal(HttpServletRequest servletRequest,
HttpServletResponse servletResponse,
FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
MultiReadRequestWrapper request = new MultiReadRequestWrapper(httpRequest);
SecurityContext context = SecurityContextHolder.getContext();
// check if already authenticated
if (context.getAuthentication() == null) {
Authentication authentication =
tokenValidationHelper.validateAndAuthenticate(request);
context.setAuthentication(authentication);
}
filterChain.doFilter(request, httpResponse);
}
}
Code for exception handler:
#ControllerAdvice
public class ExceptionHandler {
#ExceptionHandler({IrrecoverableAuthException.class})
#ResponseBody
#ResponseStatus(HttpStatus.UNAUTHORIZED)
public RegistrationErrorResponse handleInternalServerException(IrrecoverableAuthException exception) {
return getErrorResponse(exception , Category.Error exception.getMessage());
}
}
But still getting wrong message
"Full authentication access is required to access this resource"
Exception handler won't be invoked from within the filter. You can use HttpServletResponse from within the filter and write your error response manually as follows:
protected void onFailedAuthentication(
HttpServletRequest request,
HttpServletResponse response,
IrrecoverableAuthException failed) {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(failed.getStatus().getStatusCode());
try (OutputStream out = response.getOutputStream()) {
out.write(MAPPER.writeValueAsBytes(getErrorResponse())); // build the required response here
out.flush();
} catch (IOException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
}
}
Call this method from your filter
#Slf4j
#Component
public classTokenValidationFilter extends OncePerRequestFilter {
#Autowired
private TokenValidationHelper tokenValidationHelper;
#Override
protected void doFilterInternal(HttpServletRequest servletRequest,
HttpServletResponse servletResponse,
FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest)servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse)servletResponse;
MultiReadRequestWrapper request = new MultiReadRequestWrapper(httpRequest);
SecurityContext context = SecurityContextHolder.getContext();
// check if already authenticated
if (context.getAuthentication() == null) {
try {
Authentication authentication =
tokenValidationHelper.validateAndAuthenticate(request);
context.setAuthentication(authentication);
} catch(IrrecoverableAuthException ex) {
onFailedAuthentication(httpRequest, httpResponse, ex);
}
}
filterChain.doFilter(request, httpResponse);
}
}

How do I add a permission check for many methods in a controller? (Filter, Action)

I have the following at the beginning of many methods.
if ((user.privilege & User.Privilege.WRITE) == 0) {
session.setAttribute("alert", "You do not have permission to save.");
return new ModelAndView("redirect:/admin/home");
}
How can I extract this and put it into a separate method that is called before many other controller methods, similar to Ruby on Rails before_action :validate_privilege, [:save, :weekly_report, ...]?
I found this in the docs, but it doesn't give any examples.
https://docs.spring.io/spring-boot/docs/1.5.19.RELEASE/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners
I found a way to do it based on #Deadpool's answer. It seems like it is more complicated than a simple annotation would be.
#Bean
public GenericFilterBean beforeAction() {
String[] actions = new String[]{"/admin/censor", "/admin/weekly-report", "/admin/email-blast-submit"};
return new GenericFilterBean() {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// System.out.println("requestURI:"+req.getRequestURI());
boolean found = Arrays.stream(actions).anyMatch(req.getRequestURI()::equals);
if (found) {
User user = (User) req.getSession().getAttribute("user");
if (user != null && (user.privilege & User.Privilege.WRITE) == 0) {
req.getSession().setAttribute("alert", "You do not have permission to save.");
HttpServletResponse res = (HttpServletResponse) response;
res.sendRedirect("/admin/home");
return;
}
}
chain.doFilter(request, response);
}
};
}
For the API using OAuth2, I used
#Override
public ResponseEntity<...> delete(Principal principal, #RequestBody ...) throws ApiException {
isAuthorized(principal, "ROLE_USER");
...
/** If not authorized, throw ApiException. */
private void isAuthorized(Principal principal, String role) throws ApiException {
OauthClientDetail cd = userMapper.getOauthClientDetails(principal.getName());
if (cd == null || cd.authorities == null || !cd.authorities.equals(role)) {
throw new ApiException(HttpStatus.UNAUTHORIZED, ...);
}
}
(ApiException, OauthClientDetail (POJO), and UserMapper (MyBatis) are custom classes.)
If you are looking for Filter to check each request for authorization you can use Filter
#Component
public class AuthFilter implements Filter {
#Override
public void doFilter
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
LOG.info(
"Starting a transaction for req : {}",
req.getRequestURI());
chain.doFilter(request, response);
LOG.info(
"Committing a transaction for req : {}",
req.getRequestURI());
}
// other methods
}
or GenericFilterBean
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, response);
}
}
You should use Spring Security
http.regexMatcher("/*") --> your base path or sth.
.exceptionHandling()
.and()
.csrf()
.disable();
http.addFilterAfter(authenticationProcessingFilter(),BasicAuthenticationFilter.class);

Java Servlet Filter - Modify response headers based on status

I'm trying to write a servlet filter that will add headers to the response depending on the status of the request. I know I have to wrap the response with a HttpServletResponseWrapper before passing to the chain.doFilter but the headers never get sent, so I'm obviously missing something very obvious.
Code looks something like:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
HttpServletResponseWrapper responseWrapper = new HttpServletResponseWrapper(httpServletResponse);
chain.doFilter(request, responseWrapper);
if(responseWrapper.getStatus() < 400)
{
responseWrapper.addHeader("X-Custom-Foobar", "abc");
}
}
Is there something I have to capture in the wrapper to prevent the response from going out to the client until the check is complete?
So the frustrating part about this spec is that you have to completely intercept the ServletOutputStream and buffer it. I ended up following the example here :: https://stackoverflow.com/a/11027170/76343
The base class HttpServletResponseWrapper is a complete passthrough and as soon as the output stream is closed, all further modifications to the response are mute.
Unfortunately there doesn't seem to be a more elegant way to accomplish this.
You need to extend HttpResponseWrapper() and override the appropriate methods. Just using a vanilla HttpResponseWrapper by itself accomplishes exactly nothing.
The order of this code is inverted:
chain.doFilter(request, responseWrapper);
if(responseWrapper.getStatus() < 400)
{
responseWrapper.addHeader("X-Custom-Foobar", "abc");
}
Try this instead:
if(responseWrapper.getStatus() < 400)
{
responseWrapper.addHeader("X-Custom-Foobar", "abc");
}
chain.doFilter(request, responseWrapper);
The doFilter method does not return to your method until after the response has been sent on the wire.
This is actually possible. But because after calling chain.doFilter(request, response) the response is already committed, we have to set the headers after receiving the status code, but before the response is committed. Here is an example:
public class HeadersFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
chain.doFilter(request, new ResponseWrapper(response));
}
public static class ResponseWrapper extends HttpServletResponseWrapper {
public ResponseWrapper(HttpServletResponse response) {
super(response);
}
#Override
public void setStatus(int sc) {
super.setStatus(sc);
// SET YOUR HEADERS HERE
// setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
}
}
#Override
public void destroy() {
}
}

Catch httpServeletRequest in loadUserDetails method

I have a customized spring AuthenticationProvider class but try to intercept the HTTPServletRequest and HTTPServletResponse within the loadUserDetails method.
#Component("darnGoodAuthenticaionProvider")
public class DarnGoodAuthenticaionProvider
extends HandlerInterceptorAdapter
implements AuthenticationUserDetailsService {
private HttpServletRequest request;
private HttpServletResponse response;
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
this.request = request;
this.response = response;
// we don't want anything falling here
return true;
}
#Override
public UserDetails loadUserDetails(Authentication token)throws
UsernameNotFoundException{
.......
}
}
I know the preHandler method from HandlerIntercepterAdapter is capable to the job but how can I be sure that the preHandler method is called prior to loadUserDetails, so that I can get the request and response prepared?
Thanks
On a servlet container, each request will be handled from the moment the request is received until the response is returned by only one thread (request == current thread).
So it's a matter of putting a servlet filter BEFORE the spring security filter chain (with the filter-mapping element above the filter-mapping of spring security), and storing the request and response in the thread using a ThreadLocal variable - see also this answer.
Then on the DarnGoodAuthenticaionProvider access the request using a static method RequestResponseHolder.getRequest().
web.xml config:
<filter>
<filter-name>saveRequestResponseFilter</filter-name>
<filter-class>sample.save.request.filter.SaveRequestResponseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>saveRequestResponseFilter</filter-name>
<url-pattern>/mobilews/*</url-pattern>
</filter-mapping>
Filter to save the request response in the thread:
public class SaveRequestResponseFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
RequestResponseHolder.setRequestResponse(req,resp);
try {
chain.doFilter(request, response);
}
finally {
RequestResponseHolder.clear();
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
...
}
#Override
public void destroy() {
...
}
}
Request/Response holder:
public class RequestResponseHolder {
private static ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<HttpServletRequest>();
private static ThreadLocal<HttpServletResponse> responseHolder = new ThreadLocal<HttpServletResponse>();
public static void setRequestResponse(HttpServletRequest request, HttpServletResponse response) {
requestHolder.set(request);
responseHolder.set(response);
}
public static HttpServletRequest getServletRequest(){
return requestHolder.get();
}
public static HttpServletResponse getServletResponse() {
return responseHolder.get();
}
public static void clear() {
requestHolder.remove();
responseHolder.remove();
}
}
Obtaining the request from DarnGoodAuthenticaionProvider:
HttpServletRequest req = RequestResponseHolder.getServletRequest();

How can I access a request that has been wrapped in a custom class from within my Dropwizard Resource?

I am using Dropwizard and I would like to wrap a request object from within a filter, and gain access to that instance from my resource class
e.g. do the following
Filter.java
public class ServiceRequestExtractionFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
ProxyResourceRequest requestWrapper = new RequestWrapper(httpRequest, svcRequestData);
chain.doFilter(requestWrapper, response);
}
}
...
Resource.java
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public class DemoResource {
#GET
#Timed
public Response get(#Context UriInfo uriInfo, #Context RequestWrapper request) {
...
this doesn't work nor do I have any reason to expect it to, but it seemed like a good way to explain what I was trying to do.
Try this ContainerFilter documented here: http://dropwizard.io/manual/core.html#jersey-filters
public class DateNotSpecifiedFilter implements ContainerRequestFilter {
#Context ExtendedUriInfo extendedUriInfo;
#Override
public ContainerRequest filter(ContainerRequest request) {
boolean methodNeedsDateHeader = extendedUriInfo.getMatchedMethod().isAnnotationPresent(DateRequired.class);
String dateHeader = request.getHeaderValue(HttpHeaders.DATE);
if (methodNeedsDateHeader && dateHeader == null) {
Exception cause = new IllegalArgumentException("Date Header was not specified");
throw new WebApplicationException(cause, Response.Status.BAD_REQUEST);
} else {
return request;
}
}
}
Just add this in you application run():
environment.jersey().getResourceConfig().getContainerRequestFilters().add(new DateNotSpecifiedFilter());

Categories