I have an apache webserver that is used to serve php and static web files. In order to use active directory authentication i've written some code that can connect to AD through JNDI and authenticate usernames passwords and groups. What I would like is to map all requests to pages in apache through my servlet in order to make sure that a valid session is present and then if they have to login again that they have the correct AD group to visit a particular url. My issue is that when I map my servlet to every url with /* it cannot forward requests to the actual pages that I'm trying to get. It just keeps forwarding the request to my servlet and calling its doGet method till a servlet exception occurs. I want the functionality of a transparent proxy but I cannot seem to get that from this. Does anyone have any concrete examples of a transparent proxy servlet or know a way to do this with servlets. The forwarding functionality of a servlet seems to make this a perfect vehicle for doing it but I seem to be stuck.
Filter code
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
boolean authenticated = false; //should be false when testing is done.
//skip if its the login page
if(req.getRequestURI().equals("/auth/login.jsp") || authenticated){
chain.doFilter(req, res);
}else{
req.setAttribute("protectedUrl", req.getRequestURI());
res.sendRedirect("/auth/login.jsp");
}
}
Web.xml
(snip)
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
Because the servlet is mapped on /*, the RequestDispatcher#forward() will call it again, resulting in an infinite loop and finally a StackOverflowError (or some other exception depending on the servletcontainer in question which might have some recursion prevention builtin which kicks in after a certain amount of recursive calls).
After all, the Servlet is not entirely the right tool for the job, you'd rather like to use a Filter here. Implement javax.servlet.Filter and do the same job in doFilter() method. It won't call itself recursively when mapped on /* since it by default listens on requests only, not on forwards or includes.
Related
How can i create a full web application with Java and React without having to create a rest API, not even a private API with username:password authentication.
I want it to be as it is created with JSP.
Is it possible call Java methods with react locally ?
Or even creating a restfull API that can only be called locally
Thank you
I don't think it's possible to communicate with Java in a client library such as React without having to create a HTTP API.
But you could make one and add a bit of extra layer of security to ensure that only your application could call your Java API by checking the remote address of each call and verifying that's the caller is indeed your server.
You can do this in Java using the getRemoteAddr() method from the HttpServletRequest object.
The best way to do this is to create a filter class that map all the API links and verify the remote address in each calls and then decide if it should allow it or not.
Here's an example:
import javax.servlet.*;
public class RequestFilter implements Filter{
public void init(FilterConfig config) throws ServletException {}
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
String callerIp = request.getRemoteAddr();
if(callerIp.equalsIgnoreCase("MY-SERVER-IP-ADDRESS")) {
chain.doFilter(req, res);
}
else {
((HttpServletResponse)res).sendError(HttpServletResponse.SC_FORBIDDEN, "Access denied !");
return;
}
}
public void destroy() {}
}
Replace "MY-SERVER-IP-ADDRESS" with your server ip.
And to map all the calls, set the filter tag in your web.xml as follows:
<filter>
<filter-name>RequestFilter</filter-name>
<filter-class>com.myPackage.requestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RequestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
This should satisfy your need, but if you found another way please share it with us.
the first request is no, as far as I'm aware. But as for the local rest api, I know most web servers can check CORS headers and restrict to only serve certain origins on answering requests. So whichever JRE Web Server you're using, check it's API for accessing the origin in the request header, and route those to the rest code.
In short - I would like to add such service endpoints to my servlet that can only be called from localhost. The restriction should be coded in the servlet itself, i.e it should not depend on Tomcat/Apache to be configured in a certain way. At the same time, there are many other, existing endpoints that should be reachable externally.
Longer description - I am creating an HTTP API that 3rd parties can implement to integrate with my application. I am also supplying a default implementation, bundled together with my app, that customers with simple requirements can use, without having to implement anything.
The endpoints of my default implementation should be reachable only for my app, which happens to be the same servlet as the one supplying the implementation, i.e it runs on the same host. So for security reasons (the API is security related), I want my implementation to be usable only for my app, which in the first round means restricting access to localhost for a set of HTTP endpoints.
At the same time, I don't want to rely on customers setting up their container/proxy properly, but do the restriction in my servlet, so that there are no changes required for existing installations.
So far the only idea I had was to check the requestor's IP addess in a servlet filter - so I am wondering if there is a better, more sophisticated way.
I think you should add Web Filter to your application and check your url in doFilter method. Check request.getRemoteAddr() and endpoint link you can put in urlPattern.
Like this:
#WebFilter(urlPatterns = "/*")
public class RequestDefaultFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (isForbidden(request, response))
return;
else
chain.doFilter(request, response);
}
}
isForbidden implementation is up to you. In response you just send 403 error code for example.
You can check make same check in servlet and send in response 403 error.
In a working application, a Servlet filter has been placed. However this filter is causing errors in the existing application.
Currently for proof of concept purposes the filter only logs, no code has been implemented.
No exceptions are found.
On the Tomcat access log the only clue is that resources are returning an 304
[18/Nov/2014:17:18:34 -0500] 10.93.161.0 (0ms) 52D40EF309A3EB46F1C5DE3FB1D063BD.application.1 GET /secure/application/application/sc/skins/Enterprise/images/Window/window_TL.png HTTP/1.1 : 304
As mentioned without the filter in place the application works fine.
When filter is in place, logs show that the filter is logging but the application will not work properly, it seems the filter is interfering somehow, which we don't want any interference, except of implemented code in the doFilter method.
Below is the filter declaration in the web.xml
<filter>
<filter-name>SessionFilter</filter-name>
<filter-class>com.company.filter.SessionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Below is the filter class
public class SessionFilter implements Filter{
private static final Log LOG = LogFactory.getLog(SessionFilter.class);
#Override
public void destroy() {
LOG.info("SessionFilter destroyed.");
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain arg2) throws IOException, ServletException {
LOG.info("SessionFilter is filtering.");
LOG.info("Test Session");
}
#Override
public void init(FilterConfig arg0) throws ServletException {
LOG.info("SessionFilter initialized.");
}
}
Please let me know any ideas.
Thank you
In the doFilter() method of a Filter you have to chain or forward the request on the chain, else other filters will not be called and the request will not be forwarded to the Servlet.
Qutoing form the javadoc of Filter.doFilter():
A typical implementation of this method would follow the following pattern:-
Examine the request
Optionally wrap the request object with a custom implementation to filter content or headers for input filtering
Optionally wrap the response object with a custom implementation to filter content or headers for output filtering
a) Either invoke the next entity in the chain using the FilterChain object (chain.doFilter()),
b) or not pass on the request/response pair to the next entity in the filter chain to block the request processing
Directly set headers on the response after invocation of the next entity in the filter chain.
Do it like this:
chain.doFilter(request, response);
The reason why the Filter system is implemented this way is because a Filter may decide to break the chain, e.g. because sending back error; or a Filter may wrap the request and/or the response and this wrapper must be lended onward to other filters and servlets.
If you don't call chail.doFilter(), that indicates to the Servlet container that you don't want to involve other filters and servlets in serving the request.
You need to continue the filter chain. Add this to the end of the "doFilter" method.
...
// request filtered
arg2.doFilter(req, res);
// response filtered
...
I would like some help in understanding a particular behaviour of java Filters: I wrote a simple Filter which gets all user requests and, if a non-logged user requires a restricted resource, the filter forwards user to the home page. Here is my code:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// TODO Auto-generated method stub
// place your code here
HttpServletRequest req = (HttpServletRequest) request;
String uri = req.getRequestURI();
System.out.println("\n\nFILTERING...\n\n");
//Se la risorsa appartiene all'area ristretta e l'utente non รจ
//loggato lo sbatto fuori
if(uri.contains("restricted") && (req.getSession(false) == null || req.getSession(false).getAttribute("user") == null)) {
System.out.println("\n\nCannot access\n\n");
//((HttpServletResponse) response).sendRedirect("/Hotel/index.jsp");
req.getRequestDispatcher("/index.jsp").forward(request, response);
}
else {
// pass the request along the filter chain
System.out.println("\n\nNext step\n\n");
chain.doFilter(request, response);
}
}
And the mapping in the web.xml:
<filter>
<filter-name>MyFilter</filter-name>
<filter-class>mycontroller.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
What it is strange to me is that, if I ask a restricted resource via URL, the doFilter method is called several times before moving to the home (the server logs 'FILTERING... Cannot access' 4,5 times).
I'm using Tomcat 7.
Can someone help me to understand? Thanks a lot
You've mapped the filter on /*. It will thus intercept on every single HTTP request. Not only HTML/JSP pages, but also static resources like CSS, JS and image files. Apparently you've requested a HTML/JSP page which in turn references several CSS, JS and/or image files.
Your check in the filter is also pretty poor. You should rather map the filter on /restricted/*.
<url-pattern>/restricted/*</url-pattern>
Then remove that URI check from the filter's code. If you put those static resources outside that map, e.g. in /static or /resources, etc, then the filter won't be invoked for them.
HI,
I am currently running a tomcat instance with struts 1 and I would like tomcat to detect when pdf files are requested in the URL (For example of a link: http://www.***.com/files/action=download&name=myreport.pdf).
At this point I want a java class to be instantiated, then using a pdf API I want to inject a password to a file. The main point here is that I do not want to have the password store in the original pdf file I am serving instead I want the password to be injected at runtime by Tomcat.
Please let me know if you have any ideas, I did a little of research and I came across tomcat filters but I am unsure if this will resolve this problem.
Please note the passwords are store in a database table.
Thanks
From the filter we invoke a Java class to do the actual "injecting of password".
The entry in the web.xml will redirect your call to a particular filter.
<!--web.xml call all calls to .pdf will invoke the particular filter.-->
<filter>
<filter-name>PDF Filter</filter-name>
<filter-class>PDFFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>PDF Filter</filter-name>
<url-pattern>*.pdf</url-pattern>
</filter-mapping>
//This is the actual filter
public class PDFFilter implements Filter
{
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
PDFPasswordInjector pdfPassInject = new PDFPasswordInjector();
//use HttpServletRequestWrapper to get the pdf location/pdf name
pdfPassInject.injectPassword( "<pdf location>" );
chain.doFilter(request, response);
}
}
//Java class to inject the password
public class PDFPasswordInjector
{
public boolean injectPassword( String sPDFName )
{
// retrieve password from DB
// use API to inject password to PDF
}
}
Create a servlet
Set url-pattern to *.pdf
Whenever your pdf url is called, the servlet is executed.
Do whatever you want from the servlet before returning the PDF to the user in response.
It should be fairly straight forward to write a Filter to intercept all requests that return a PDF. Filter's doFilter() method has access to the request and response so you can modify it however you like.
Filters are not the way to solve this particular problem. Filters allow you to modify requests and cause them to be redirected or redispatched to different servlets. But they don't allow you to rewrite the response body. From what I understand, this is what you are trying to do.
You will have to do the PDF file modification in a Servlet, as described in #Aardvocate's answer.