Servlet filter change response? - java

I have below servlet filter.
public class MyFilter extends BaseServletRequestFilter {
#Override
protected void afterExecutingFilterChain(final ServletRequest requset, FilterResponseWrapper response) throws ServletException {
//To do
}
#Override
protected void beforeExecutingFilterChain(final ServletRequest requset, final FilterResponseWrapper response) throws ServletException{
//Here request needs to be intercepted
//To do
}
}
I have abover filter. My requirement is i need to intercept the request. I need to check some boolean value in the request. If boolean variable is true then request processing should be continued. If boolean variale is false then request should not continue and i need to send some custom response as below.
public enum CustomStatus {
OK("Ok"),
BAD_REQUEST("BadRequest");
private final String value;
CustomStatus(String v) {
value = v;
}
public String value() {
return value;
}
public static CustomStatus fromValue(String v) {
for (CustomStatus c: CustomStatus.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
If request boolean variable's value is false then i have to set above custom status into response and return without processing the request. How can i do that?
Thanks!

If you create a Filter by extending Filter, you can do:
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
if(your status is ok) {
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).sendError(the error code,
"the error message" );
}
}

Use the Filter interface:
public final class XssFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException
{
//check request...
if (ok) {
chain.doFilter(request, response);
} else {
// do something with the response
}
}
Can't be more specific that that, because you don't say exactly where the boolean value you are checking is (is a parameter, or part of the URL, or a cookie, or a header?), neither do you say exactly what you want done with the response.

Related

how to allow access to login page without authentication from filter

the filter is always checking for authentication for the login page and I don't know how to configure it.
here is the AppFilter.java (I couldn't post all the code):
#Singleton
public class AppFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
try {
final String appLocale = AppSettings.get().get(AvailableAppSettings.APPLICATION_LOCALE, null);
APP_LOCALE = appLocale == null ? null : new Locale(appLocale);
} catch (Exception e) {
}
}
public static String getBaseURL() {
return BASE_URL.get();
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
BASE_URL.set(computeBaseUrl(request));
LANGUAGE.set(request.getLocale());
try {
chain.doFilter(request, response);
} finally {
LANGUAGE.remove();
BASE_URL.remove();
}
}
}
You can modify doFilter() in order to check if the requested URL belongs to the list of predefined excluded URLs, if so then just forward the request to the next filter or servlet in the chain, otherwise do whatever you want to do.
Here is the code:
#Singleton
public class AppFilter implements Filter {
private List<String> excludedUrls;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
excludedUrls = Arrays.asList(YOUR_LOGIN_URL);
Filter.super.init(filterConfig);
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String path = ((HttpServletRequest) servletRequest).getServletPath();
if(!excludedUrls.contains(path))
{
// if url does not match your login url do what you want to do..
}
// else forward the request to the next filter or servlet in the chain.
chain.doFilter(req, resp);
}
#Override
public void destroy() {
}
}

The servlet container, the servlet url pattern and request wrappers

I was working on a project where based on the presence of a particular string in the request URL to a web application, I was trying to modify the URL of the request using a request wrapper.
I learnt that even if I override the getRequestURI, getRequestURL and getServletPath methods in the wrapper and send that wrapper from a filter, the servlet container still uses its own implementation of the ServletRequest interface to figure out which servlet to call.
I believe the container uses the stored variable for the request URI in the implementation class of ServletRequest and doesn't actually call any of the getRequestURI, getRequestURL and getServletPath methods for identifying the servlet to use (matching URL pattern).
Need all your inputs and knowledge to learn more about this topic. Please help me learn this topic better. Thanks..!!
Below are my experimental code and the request comes from the jsp for http://localhost:8080/RequestResponseListenersWebApp/pages/abcd.jsp
The Filter
#WebFilter(filterName = "AllRequestScanFilter", urlPatterns = "/*")
public class AllRequestScanFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
EmployerViewTestBusinessWrapper wrapper = new EmployerViewTestBusinessWrapper(request);
if (request.getRequestURI().contains("abcd.jsp"))
chain.doFilter(wrapper, resp);
else {
chain.doFilter(req, resp);
}
}
public void init(FilterConfig config) throws ServletException {
}
}
The Wrapper
public class EmployerViewTestBusinessWrapper extends HttpServletRequestWrapper {
public EmployerViewTestBusinessWrapper(HttpServletRequest request) {
super(request);
}
#Override
public String getPathTranslated() {
return super.getPathTranslated();
}
#Override
public String getRequestURI() {
String currentPath=pathModified(super.getRequestURI());
return currentPath!=null?currentPath:super.getRequestURI();
}
#Override
public StringBuffer getRequestURL() {
String currentPath=pathModified(super.getRequestURL().toString());
return currentPath!=null?new StringBuffer(currentPath):new StringBuffer(super.getRequestURL());
}
#Override
public String getServletPath() {
String currentPath=pathModified(super.getServletPath());
return currentPath!=null?currentPath:super.getServletPath();
}
private String pathModified(String currentPath){
String returnPath=null;
if(currentPath.contains("pages")){
returnPath=currentPath.replaceFirst("/pages/","/pages/myapp/");
}
return returnPath;
}
}
The Servlet which is never reached
#WebServlet(name = "EmployerViewTestServlet",urlPatterns = "/pages/myapp/*")
public class EmployerViewTestServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path=request.getParameter("path");
}
}
And one more thing. The Filter I have specified here is the first thing in the web app that's called when a request comes in.
I can always check the URL pattern in the filter and forward the request to the desired URL pattern using a RequestDispatcher.
However, in my app there're some Filters that are called for dispatcher type FORWARD and I don't want them to be called when a request has just come from the client.
Wouldn't be this what you're looking for?
In that case, the request.getRequestDispatcher(newURI).forward(req, res) seems to do the trick, using only the filter.
#WebFilter(filterName = "AllRequestScanFilter", urlPatterns = "/*")
public class AllRequestScanFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
//EmployerViewTestBusinessWrapper wrapper = new EmployerViewTestBusinessWrapper(request);
String URI = request.getRequestURI();
if (URI.contains("abcd.jsp") && URI.contains("pages")) {
URI = URI.replaceFirst("/pages/","/pages/myapp/");
request.getRequestDispatcher(URI).forward(req, res);
} else {
chain.doFilter(req, resp);
}
}
public void init(FilterConfig config) throws ServletException {
}
}

Servlet filter "proxy" that only acts on response from remote endpoint

I have a need where certain HTTP requests must be redirected to a Spring Boot web app/service, but that on the request-side, the Spring app does nothing and acts as a passthrough between the HTTP client (another service) and the request's true destination. But when the response comes back to the Spring app (from that destination), I need the Spring app to be able to inspect the response and possibly take action on it if need be. So:
HTTP client makes a request to, say, http://someapi.example.com
Network magic routes the request to my Spring app at, say, http://myproxy.example.com
On the request, this app/proxy does nothing, and so the request is forwarded on http://someapi.example.com
The service endpoint at http://someapi.example.com returns an HTTP response back to the proxy
The proxy at http://myproxy.example.com inspects this response, and possibly sends an alert before returning the response back to the original client
So essentially, a filter that acts as a pass-through on the request, and only really does anything after the remote service has executed and returned a response.
My best attempt thus far has been to setup a servlet filter:
#Override
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response)
// How and where do I put my code?
if(responseContainsFizz(response)) {
// Send an alert (don't worry about this code)
}
}
Is this possible to do? If so, where do I put the code that inspects and acts upon the response? With my code the way it is I get exceptions thrown when trying to hit a controller from a browser:
java.lang.IllegalStateException: STREAM
at org.eclipse.jetty.server.Response.getWriter(Response.java:910) ~[jetty-server-9.2.16.v20160414.jar:9.2.16.v20160414]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_92]
rest of stack trace omitted for brevity
Any ideas?
Per the Servlet API documentation, the reason you are getting the IllegalStateException is because you are attempting to call ServletResponse.getWriter after ServletResponse.getOutputStream has already been called on the response. So it appears that the method you need to call is ServletResponse.getOutputStream().
However, if you are trying to access the body of the response, the best solution is to wrap the response in a ServletResponseWrapper so that you can capture the data:
public class MyFilter implements Filter
{
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
#Override
public void destroy()
{
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
MyServletResponseWrapper responseWrapper = new MyServletResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, responseWrapper);
if (evaluateResponse(responseWrapper)) {
// Send an alert
}
}
private boolean evaluateResponse(MyServletResponseWrapper responseWrapper) throws IOException
{
String body = responseWrapper.getResponseBodyAsText();
// Perform business logic on the body text
return true;
}
private static class MyServletResponseWrapper extends HttpServletResponseWrapper
{
private ByteArrayOutputStream copyOutputStream;
private ServletOutputStream wrappedOutputStream;
public MyServletResponseWrapper(HttpServletResponse response)
{
super(response);
}
public String getResponseBodyAsText() throws IOException
{
String encoding = getResponse().getCharacterEncoding();
return copyOutputStream.toString(encoding);
}
#Override
public ServletOutputStream getOutputStream() throws IOException
{
if (wrappedOutputStream == null) {
wrappedOutputStream = getResponse().getOutputStream();
copyOutputStream = new ByteArrayOutputStream();
}
return new ServletOutputStream()
{
#Override
public boolean isReady()
{
return wrappedOutputStream.isReady();
}
#Override
public void setWriteListener(WriteListener listener)
{
wrappedOutputStream.setWriteListener(listener);
}
#Override
public void write(int b) throws IOException
{
wrappedOutputStream.write(b);
copyOutputStream.write(b);
}
#Override
public void close() throws IOException
{
wrappedOutputStream.close();
copyOutputStream.close();
}
};
}
}
}
The response can be easy manipulated/replaced/extended e with a filter and a response wrapper.
In the filter before the call chain.doFilter(request, wrapper) you prepare a PrintWriter for the new response content and the wrapper object.
After the call chain.doFilter(request, wrapper) is the actuall response manipulation.
The wrapper is used to get access to the response as String.
The Filter:
#WebFilter(filterName = "ResponseAnalysisFilter", urlPatterns = { "/ResponseFilterTest/*" })
public class ResponseFilter implements Filter {
public ResponseFilter() {}
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
PrintWriter out = response.getWriter();
CharResponseWrapper wrapper = new CharResponseWrapper((HttpServletResponse) response);
chain.doFilter(request, wrapper);
String oldResponseString = wrapper.toString();
if (oldResponseString.contains("Fizz")) {
// replace something
String newResponseString = oldResponseString.replaceAll("Fizz", "Cheers");
// show alert with a javascript appended in the head tag
newResponseString = newResponseString.replace("</head>",
"<script>alert('Found Fizz, replaced with Cheers');</script></head>");
out.write(newResponseString);
response.setContentLength(newResponseString.length());
}
else { //not changed
out.write(oldResponseString);
}
// the above if-else block could be replaced with the code you need.
// for example: sending notification, writing log, etc.
out.close();
}
}
The Response Wrapper:
public class CharResponseWrapper extends HttpServletResponseWrapper {
private CharArrayWriter output;
public String toString() {
return output.toString();
}
public CharResponseWrapper(HttpServletResponse response) {
super(response);
output = new CharArrayWriter();
}
public PrintWriter getWriter() {
return new PrintWriter(output);
}
}
The Test Servlet:
#WebServlet("/ResponseFilterTest/*")
public class ResponseFilterTest extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
response.getWriter().append(
"<html><head><title>replaceResponse filter</title></head><body>");
if (request.getRequestURI().contains("Fizz")) {
response.getWriter().append("Fizz");
}
else {
response.getWriter().append("Limo");
}
response.getWriter().append("</body></html>");
}
}
Test Urls:
https://yourHost:8181/contextPath/ResponseFilterTest/Fizz (Trigger response Replacement)
https://yourHost:8181/contextPath/ResponseFilterTest/ (response unchanged)
More Info and examples about filters:
http://www.oracle.com/technetwork/java/filters-137243.html#72674
http://www.leveluplunch.com/java/tutorials/034-modify-html-response-using-filter/
https://punekaramit.wordpress.com/2010/03/16/intercepting-http-response-using-servlet-filter/

Forcing Spring to return status 200 on HEAD requests

I need to make a Filter that will catch all HEAD requests and will return status 200 on them.
As I undertand, I need to create a Filter that will catch every HEAD request, which is done, and do something to return 200 on every requests, which is a question.
I mean filter catches request and able to do something with it, but I need not a request, but a response that will be 200. So what else can I do?
public class HttpHeadFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (isHttpHead(httpServletRequest)) {
chain.doFilter(new ForceHeadToOkStatusWrapper (httpServletRequest), response);
} else {
chain.doFilter(request, response);
}
}
public void destroy() {
}
private boolean isHttpHead(HttpServletRequest request) {
return "HEAD".equals(request.getMethod());
}
private class ForceHeadToOkStatusWrapper extends HttpServletRequestWrapper {
public ForceGetRequestWrapper(HttpServletRequest request) {
super(request);
}
//somethig here
}
}
Finally I've created an interceptor:
public class HttpHeadInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (isHttpHead(request)) {
response.setStatus(HttpStatus.SC_OK);
return false;
}
return true;
}
private boolean isHttpHead(HttpServletRequest request) {
return HttpMethod.HEAD.equals(request.getMethod());
}
}
And added it to WebMvcConfigurerAdapter:
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HttpHeadInterceptor());
}
And it works smooth)

How to create multiple asynchronous java filters?

I'm trying to create a Java application with multiple asynchronous filters, but cannot seem to get them to work well together. I think the main issue is in the run() method I don't know what to do to pass along the request to the next filter in the chain. I've tried chain.doFilter(request, response), but that doesn't seem to work, and there are dispatch() and complete() APIs available on the AsyncContext, but those seem to close out the entire AsyncContext. It seems like there must be another way to get this to work. Below is a snippet of the filter I'm using - the second filter looks almost identical.
Note: I'm adding headers to try and figure out what is getting called.
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
final AsyncContext asyncContext = request.startAsync();
final HttpServletResponse res = (HttpServletResponse) response;
asyncContext.addListener(new AsyncListener() {
#Override
public void onComplete(AsyncEvent event) throws IOException {
res.addHeader("S-AST2", "onComplete");
}
#Override
public void onTimeout(AsyncEvent event) throws IOException {
res.addHeader("S-AST3", "onTimeout");
}
#Override
public void onError(AsyncEvent event) throws IOException {
res.addHeader("S-AST4", "onError");
}
#Override
public void onStartAsync(AsyncEvent event) throws IOException {
res.addHeader("S-AST0", "onStartAsync");
}
});
asyncContext.start(new Runnable() {
#Override
public void run() {
res.addHeader("S-AST1", "before");
// This doesn't seem to work...
asyncContext.dispatch();
// ... or this ...
asyncContext.complete();
// ... or this ...
chain.doFilter(request, response);
}
});
}
Thanks for any insight!
There are two parts to this answer.
1) The chain.doFilter(request, response); is still required.
2) The reason this was not working is that in each filter and in the servlet I was calling request.startAsync(), which started a new async process, rather than using an existing one. So if the filter started an async process, and the servlet also started one, it would overwrite/ignore the one started in the filter. To solve this you must check to see if an async process is already started, by calling request.isAsyncStarted(), and if it is, rather than starting a new async context, you should get the existing one with request.getAsyncContext(). Below is a helper class I created to do this for each servlet and filter, so that I can just call AsyncHelper.getAsyncContext(request, response) and it will either retrieve the existing AsyncContext, or create a new one.
public class AsyncHelper {
public static AsyncContext getAsyncContext(ServletRequest request, ServletResponse response) {
AsyncContext asyncContext = null;
if (request.isAsyncStarted()) {
asyncContext = request.getAsyncContext();
}
else {
asyncContext = request.startAsync(request, response);
asyncContext.setTimeout(2000);
}
return asyncContext;
}
}
I had the need to decorate the response, and I did not know whether the underlying servlet was doing async or not, or if it already had completed. On Jetty 9.1.x I solved it by expecting IllegalStateException
The following example illustrates how to wrap the response (Using the custom BufferingHttpServletResponseWrapper that buffers all that is written to the response) to intercept input so that it can be decorated.
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// Buffer the output to a string in order to calculate its signature and add the signature to a header before it's sent to the client
final BufferingHttpServletResponseWrapper responseWrapper = new BufferingHttpServletResponseWrapper(httpServletResponse);
chain.doFilter(httpServletRequest, responseWrapper);
// This is the only way I can see that will safely let us know if we should treat this as an active async request or not.
try {
httpServletRequest.getAsyncContext().addListener(new AsyncListener() {
#Override
public void onComplete(AsyncEvent event) throws IOException {
LOG.debug("onComplete {}", event);
decorateResponse(responseWrapper);
}
#Override
public void onTimeout(AsyncEvent event) throws IOException {
LOG.debug("onTimeout {}", event);
}
#Override
public void onError(AsyncEvent event) throws IOException {
LOG.debug("onError {}", event);
}
#Override
public void onStartAsync(AsyncEvent event) throws IOException {
LOG.debug("onStartAsync {}", event);
event.getAsyncContext().addListener(this);
}
}
, httpServletRequest, responseWrapper);
LOG.debug("After chain.doFilter, async was started");
} catch (IllegalStateException e) {
LOG.debug("Async not active it appears... {}", e.getMessage());
decorateResponse(responseWrapper);
}
}

Categories

Resources