I was reading that "With every Request to your web application, client's IP is sent too. So all you need to do is to have Filter over Requests and you can store the IP. "
If this is so,how can I do this ? I mean what is the method that can tell me the IP sent in the request ?
Create a Filter class that implements javax.servlet.Filter, and fetch the IP from ServletRequest using getRemoteAddr():
public final class ExtractIpFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void destroy() {
this.filterConfig = null;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String ip = request.getRemoteAddr();
// do something with the IP
}
}
If your client is behind a proxy, try using request.getHeader("x-forwarded-for") instead, though this may or may not work depending on the configuration of the proxy.
Related
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/
I have custom redirection filters in my project, which are loading defined rules from database, and handle redirections.
In addition to those, I use also UrlRewriteFilter , with some rules defined in urlrewrite.xml.
Now I want to switch completely to use tuckey's filter, however I want to load rules from my database, instead of having them defined in xml (so that I can have single place to keep redirection rules).
My idea is to extend UrlRewriteFilter, and instead of initializing rules from XML files, load my rules from database.
Here is what I have so far,
#Service
public class CustomUrlRewriteFilter extends UrlRewriteFilter {
#Autowired
private RedirectService redirectService;
private UrlRewriter urlRewriter;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
}
#Override
protected UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) {
return urlRewriter;
}
#PostConstruct
public void initUrlRewriter() {
List<Redirect> redirects = redirectService.retrieveAll();
Conf conf = new Conf();
for (Redirect redirect : redirects) {
NormalRule rule = new NormalRule();
rule.setMatchType(redirect.getMatchType());
rule.setToType(redirect.getRedirectType());
rule.setFrom(redirect.getPath());
rule.setTo(redirect.getTarget());
rule.setQueryStringAppend("true");
conf.addRule(rule);
}
conf.setUseQueryString(true);
urlRewriter = new UrlRewriter(conf);
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
super.doFilter(req, resp, chain);
}
}
The problem is, redirection is not happening, I do not get any exception. When I debug, I can see that when processing request it returns false, as it can't find a rule chain (could it be that I need to init somehow rule chain?).
I assume I miss something to override, or my initialization is wrong. Does anyone has an experience on this? Is it possible to achieve proper behavior at all?
Any help is appreciated.
So finally I could manage to get this work.
The problem was that adding rules to the Conf is not enough, I had to also initilize those rules by calling conf.initialise().
On the other side, conf.initialise() itself calls NormalRule.initialise() which is checking the existence and validity of urlrewrite.xml, and if it does not properly initilized it returns false and the configuration is not initilizing properly.
The workaround is to provide custom rule extending NormalRule.initialise(), which explicitly returns always true when calling it's initialise().
Here is the example of custom rule
public final class CustomRule extends NormalRule {
public CustomRule(RedirectType redirectType, MatchType matchType, String from, String to) {
super();
setMatchType(matchType);
setToType(redirectType);
setFrom(from);
setTo(to);
setQueryStringAppend("true");
}
#Override
public boolean initialise(ServletContext context) {
super.initialise(context);
return true;
}
}
And then when creating UrlRewriter I need to initiliaze the configuration, and that's it.
#Service
public class CustomUrlRewriteFilter extends UrlRewriteFilter {
#Autowired
private RedirectService redirectService;
private UrlRewriter urlRewriter;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
super.init(filterConfig);
}
#Override
protected UrlRewriter getUrlRewriter(ServletRequest request, ServletResponse response, FilterChain chain) {
return urlRewriter;
}
#PostConstruct
public void initUrlRewriter() {
List<Redirect> redirects = redirectService.retrieveAll();
Conf conf = new Conf();
for (Redirect redirect : redirects) {
CustomRule rule = new CustomRule(
redirect.getRedirectType(), redirect.getMatchType(), redirect.getPath(), redirect.getTarget()
);
conf.addRule(rule);
}
conf.setUseQueryString(true);
conf.initialise();
urlRewriter = new UrlRewriter(conf);
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException,
ServletException {
super.doFilter(req, resp, chain);
}
#Override
public void destroy() {
super.destroy();
}
}
Still: Comments are welcomed.
In my case, I wanted to use a property value to build a rule. I found a shorter version to achieve the same as the above post.
#Component
public class CustomUrlRewriteFilter extends UrlRewriteFilter {
#Value("${sites.cities.codes}")
private String citiesCodes;
#Override
protected void loadUrlRewriter(FilterConfig filterConfig) throws ServletException {
Conf conf = new Conf();
NormalRule rule = new NormalRule();
rule.setMatchType("regex");
rule.setName("Remove City param");
String from = "^/(" + citiesCodes + ")/?(.*)$";
rule.setFrom(from);
rule.setTo("/$2");
conf.addRule(rule);
conf.initialise();
checkConf(conf);
}
}
I am attempting to use a filter to check for HTML tags in a response body. The problem is that if I alter the body in the filter, it isn't altered when it gets to the client. I tried the solution shown here: Looking for an example for inserting content into the response using a servlet filter
but it didn't help.
I have two filters. SecureWrapperFilter wraps the request/response objects in our custom wrapper, and XSSFilter uses OWASP encode to encode for html content. The filters look like this:
public class SecureWrapperFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(final ServletRequest request, final ServletResponse response,
final FilterChain chain) throws IOException, ServletException
{
final ServletRequestWrapper securityRequest =
new ServletRequestWrapper((HttpServletRequest)request);
final ServletResponseWrapper securityResponse =
new ServletResponseWrapper((HttpServletResponse)response);
ESAPI.httpUtilities().setCurrentHTTP(securityRequest, securityResponse);
chain.doFilter(ESAPI.currentRequest(), ESAPI.currentResponse());
}
#Override
public void destroy() {
}
}
and:
public class XSSFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(final ServletRequest request, final ServletResponse response,
final FilterChain chain) throws IOException, ServletException
{
final ServletRequestWrapper requestWrapper = (ServletRequestWrapper)request;
final String body = Encode.forHtmlContent(requestWrapper.getBody());
requestWrapper.setBody(body);
chain.doFilter(requestWrapper, response);
final ServletResponseWrapper responseWrapper = (ServletResponseWrapper)response;
final byte[] copy = responseWrapper.getCopy();
final String oldBody = new String(copy, response.getCharacterEncoding());
final String newBody = Encode.forHtmlContent(oldBody);
if (!StringUtils.equals(oldBody, newBody)) {
responseWrapper.getResponse().getOutputStream().write(newBody.getBytes());
}
}
#Override
public void destroy() {
}
}
If I add some debug Logging, I can see that the securityResponse has the modified body in the SecureWrapperFilter, but on the client side, the body looks as if it was never modified.
Any suggestions would be greatly appreciated. Thanks.
The problem was that in my XSSFilter, I was appending the new response body onto the old one. This was causing invalid json like {"x"="y"}{"escapedx"="escapedy")
Our client deserializer was only printing the first json object so {"x"=y"} was all we were seeing on the client side.
To resolve this problem, I added the following line to the XSSFilter:
responseWrapper.getResponse().resetBuffer();
before
responseWrapper.getResponse().getOutputStream().write(newBody.getBytes());
This clears the buffer, allowing me to rewrite it on the line below. My json on the client side now looks like: {"escapedx"="escapedy"}
You need to make sure the HttpResponse is buffered. If the buffer is not big enough, then the reponse will be streamed to the client befire your filter is called.
Or maybe the servler is calling flush() on the response?
Sending back json can be done with jackson:
val res = response as HttpServletResponse
res.status = HttpStatus.UNAUTHORIZED.value()
res.contentType = MediaType.APPLICATION_JSON_UTF8_VALUE
res.outputStream.write(ObjectMapper().writeValueAsString(ResponseError(
"two_factor_auth_failed", "Two Factor Authorization is required to proceed."
)).toByteArray())
I use Spring Security 3. I have follewing method:
public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, authResult);
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authResult;
WebAuthenticationDetails details = (WebAuthenticationDetails) token.getDetails();
String address = details.getRemoteAddress();
System.out.println("Successful Login from remote address: "+ address);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
super.unsuccessfulAuthentication(request, response, failed);
System.out.println("==failed login==");
}
}
Do I have a dirty code for unboxing adress variable? Can I write it shortly or properly?
This will be probably better:
String address = request.getRemoteAddr();
What exactly are you looking for? That "address" will be the reported address of what/who requested your service, though it can be spoofed, and proxies will report themselves as the "remote address", optionally including an HTTP header including the original "remote address". By convention, the header is named "X-FORWARDED-FOR". It's completely up to the proxy and whoever configured it, though.
How can I use a servlet filter to change an incoming servlet request url from
http://nm-java.appspot.com/Check_License/Dir_My_App/Dir_ABC/My_Obj_123
to
http://nm-java.appspot.com/Check_License?Contact_Id=My_Obj_123
?
Update: according to BalusC's steps below, I came up with the following code:
public class UrlRewriteFilter implements Filter {
#Override
public void init(FilterConfig config) throws ServletException {
//
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
String requestURI = request.getRequestURI();
if (requestURI.startsWith("/Check_License/Dir_My_App/")) {
String toReplace = requestURI.substring(requestURI.indexOf("/Dir_My_App"), requestURI.lastIndexOf("/") + 1);
String newURI = requestURI.replace(toReplace, "?Contact_Id=");
req.getRequestDispatcher(newURI).forward(req, res);
} else {
chain.doFilter(req, res);
}
}
#Override
public void destroy() {
//
}
}
The relevant entry in web.xml look like this:
<filter>
<filter-name>urlRewriteFilter</filter-name>
<filter-class>com.example.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>urlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
I tried both server-side and client-side redirect with the expected results. It worked, thanks BalusC!
Implement javax.servlet.Filter.
In doFilter() method, cast the incoming ServletRequest to HttpServletRequest.
Use HttpServletRequest#getRequestURI() to grab the path.
Use straightforward java.lang.String methods like substring(), split(), concat() and so on to extract the part of interest and compose the new path.
Use either ServletRequest#getRequestDispatcher() and then RequestDispatcher#forward() to forward the request/response to the new URL (server-side redirect, not reflected in browser address bar), or cast the incoming ServletResponse to HttpServletResponse and then HttpServletResponse#sendRedirect() to redirect the response to the new URL (client side redirect, reflected in browser address bar).
Register the filter in web.xml on an url-pattern of /* or /Check_License/*, depending on the context path, or if you're on Servlet 3.0 already, use the #WebFilter annotation for that instead.
Don't forget to add a check in the code if the URL needs to be changed and if not, then just call FilterChain#doFilter(), else it will call itself in an infinite loop.
Alternatively you can also just use an existing 3rd party API to do all the work for you, such as Tuckey's UrlRewriteFilter which can be configured the way as you would do with Apache's mod_rewrite.
You could use the ready to use Url Rewrite Filter with a rule like this one:
<rule>
<from>^/Check_License/Dir_My_App/Dir_ABC/My_Obj_([0-9]+)$</from>
<to>/Check_License?Contact_Id=My_Obj_$1</to>
</rule>
Check the Examples for more... examples.
A simple JSF Url Prettyfier filter based in the steps of BalusC's answer. The filter forwards all the requests starting with the /ui path (supposing you've got all your xhtml files stored there) to the same path, but adding the xhtml suffix.
public class UrlPrettyfierFilter implements Filter {
private static final String JSF_VIEW_ROOT_PATH = "/ui";
private static final String JSF_VIEW_SUFFIX = ".xhtml";
#Override
public void destroy() {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = ((HttpServletRequest) request);
String requestURI = httpServletRequest.getRequestURI();
//Only process the paths starting with /ui, so as other requests get unprocessed.
//You can register the filter itself for /ui/* only, too
if (requestURI.startsWith(JSF_VIEW_ROOT_PATH)
&& !requestURI.contains(JSF_VIEW_SUFFIX)) {
request.getRequestDispatcher(requestURI.concat(JSF_VIEW_SUFFIX))
.forward(request,response);
} else {
chain.doFilter(httpServletRequest, response);
}
}
#Override
public void init(FilterConfig arg0) throws ServletException {
}
}
In my case, I use Spring and for some reason forward did not work with me, So I did the following:
public class OldApiVersionFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
if (httpServletRequest.getRequestURI().contains("/api/v3/")) {
HttpServletRequest modifiedRequest = new HttpServletRequestWrapper((httpServletRequest)) {
#Override
public String getRequestURI() {
return httpServletRequest.getRequestURI().replaceAll("/api/v3/", "/api/");
}
};
chain.doFilter(modifiedRequest, response);
} else {
chain.doFilter(request, response);
}
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void destroy() {}
}
Make sure you chain the modifiedRequest