how to prevent servlet from being invoked directly through browser - java

I was working on a web project using java servlet and jsp pages. In one of the servlet we have RequestDispatcher method and which is calling another servlet.
#WebServlet("/Demo")
public class DemoServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.sendRedirect("testing"); //calling other servlet
}
}
#WebServlet("/testing")
public class TestingServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("Hello World");
}
}
So, now I wanted to prevent contextRoot/testing from being invoked directly from the browser but instead only let it invoked from the other servlet(Demo)
Please suggest me if there is any way to do that.

Couple of techniques exist:
Look at writing a HTTP Request Filter. You can then inspect the incoming request and the url and reject it if the pattern matches the servlet paths that you do not want to be invoked directly.
Another mechanism is to use the security constraints in your web.xml to allow access to various paths in your application only to authorized users/roles. Look at <security-constraint> tag in web.xml

Answer given by "Romin" is correct. You have to use Filters for this. what you can do is, you can set a new session variable whenever "/Demo" url is accessed and in the filter check for the condition that session exists, if it exists allow the url or else throw error. You could do something similar like this. In "Demo" servlet
#WebServlet("/Demo")
public class DemoServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
HttpSession session = request.getSession() //get new session
res.sendRedirect("testing"); //calling other servlet
}
}
In Filter class add the below code
#WebFilter("/login")
public class MyFilter implements Filter{
public void init(FilterConfig arg0) throws ServletException {}
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpRequest request = (HttpRequest) req;
HttpResponse respone = (HttpResponse) res;
HttpSession session = request.getSession(false) //get the existing session object
if(null != session) {
chain.doFilter(req, resp);
} else {
"redirect to some error page or home page"
}
}
public void destroy() {}
}

One approach is to check the caller's ip using ServletRequest.getRemoteAddr() and rejects it if it's not called locally
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
if(!req.getRemoteAddr().equals("127.0.0.1")) { // reject }
}
However this method wouldn't work legitimate caller (eg: proxy) is also using the same ip.

Related

Call ContainerRequestFilter on Tomcat's own DefaultServlet

Following BalusC's instructions on that answer:
How to stream audio/video files such as MP3, MP4, AVI, etc using a Servlet
I added the following Context element to my Tomcat server.xml to make my media files available to Tomcat's own DefaultServlet.
<Context docBase="/home/jwi/media" path="/service/media" />
This works like charm and the media is available at:
http://localhost:8080/service/media/example.mp4
The ApplicationPath from my application (build on Jersey 2.x) is set to: #ApplicationPath("service").
Within that application I have a request filter that checks every incoming request for a valid user session.
#Provider
#PreMatching
#Priority(1)
public class SessionFilter implements ContainerRequestFilter {
#Context
private ServletContext _context;
#Context
private HttpServletRequest _request;
#Context
private HttpServletResponse _response;
public void filter(ContainerRequestContext requestContext) throws IOException {
HttpSession session = _request.getSession(false);
boolean isLoggedIn = session != null && session.getAttribute("username") != null;
boolean isLoginRequest = _request.getRequestURI().contains("login");
if (isLoggedIn || isLoginRequest) {
// Since filter chain is invoked by #Priority annotation here's nothing to do.
} else {
URI indexPage = UriBuilder.fromUri("/index.html").build();
requestContext.abortWith(Response.temporaryRedirect(indexPage).build());
}
}
}
My problem is, that filter is never called on the media elements. So when I open http://localhost:8080/service/media/example.mp4 the filter isn't called at all.
How do I add Tomcat's DefaultServlet to my request filter?
Have you considered a Servlet Filter instead?
#WebFilter("/service/media/*")
public class SessionFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}

Spring boot HandlerInterceptor loadbalancing

I'm implementing a (sort of) load balancing HandlerInterceptor using Spring Boot.
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
if (shouldUseServer1(uri)) {
response.sendRedirect(server1Uri);
} else {
response.sendRedirect(server2Uri);
}
}
The idea is, that based on the url, we either redirect to one service or another. The application doesn't have any explicit RequestMappings (yet).
Now the problem is, when the interceptor is called, the request is redirected to the default Spring error handler. As a result the URI stored in the HttpServletRequest is replaced by /error (effectively denying the access to the original URI).
Is there any way to intercept a request before it is rerouted to the error handler (or to get the original uri)?
EDIT:
Because of the way Spring MVC handles requests with no mapping, you'll either need a filter:
#Component
public class CustomFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
request.getSession().setAttribute("ORIGINAL_REQUEST_URI", request.getRequestURI());
chain.doFilter(request, response);
// alternatively, ignore the last 2 lines
// and just do your redirects from here
// and don't continue the filter chain
}
#Override
public void destroy() {}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}
Otherwise, if you'd rather not rely on the session, you'll need to make the DispatcherServlet throw an exception in case no handler mapping is found, and then send the redirect from a #ControllerAdvice error handler:
#ControllerAdvice
class NoHandlerFoundExceptionExceptionHandler {
#ExceptionHandler(value = NoHandlerFoundException.class)
public ModelAndView
defaultErrorHandler(HttpServletRequest req, NoHandlerFoundException e) throws Exception {
String uri = // resolve the URI
return new ModelAndView("redirect:" + uri);
}
}
To avoid duplication, you may want to have a common class that you'll call from both the interceptor and the error handler.

Inheriting Servlet instead of using filters

By reading this answer, I noted that the given exemple :
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
if (((HttpServletRequest) request).getSession().getAttribute("user") == null) {
// User is not logged in. Redirect to login page.
((HttpServletResponse) response).sendRedirect("login");
} else {
// User is logged in. Just continue with request.
chain.doFilter(request, response);
}
}
make us having to write in web.xml something like :
<filter>
<filter-name>RestrictionFilter</filter-name>
<filter-class>com.myproject.filters.RestrictionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RestrictionFilter</filter-name>
<url-pattern>/restrained/*</url-pattern>
</filter-mapping>
But I want to be totally free to choose which url have to be restrained to loged members (it's a bit annoying to specify, for each url-pattern, the filter which have to be activated).
I used to create a Servlet who inherits HttpServlet and surcharges service() in order to check if the session contains a member instance, and then, calls the "true" service() method :
public abstract class MembreHttpServletProjet1 extends HttpServlet{
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
if(req.getSession().getAttribute("membre") == null){
resp.sendRedirect(req.getContextPath() + "/accueil");
return;
}
super.service(req, resp);
}
}
By this way, if a servlet needs to be used only if the user is loged as a member, I make inherit this servlet by MembreHttpServletProjet1.
Is this way wrong ? I understood the utility of filters in some case but I still confused for this exemple.
Thank you.

Java Servlet return error 405 (Method Not Allowed) for POST request

My servet work fine for get requests but when I call POST (using jquery ajax $.post) I get error 405 (Method Not Allowed)
Here is my code:
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class init extends HttpServlet {
public init() { }
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/plain");
PrintWriter out = response.getWriter();
out.println("GET");
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException, IllegalStateException {
response.setContentType("application/json");
ServletInputStream in = request.getInputStream();
PrintWriter out = response.getWriter();
out.print("POST");
}
}
This happened to me when my method call
"super.doGet(req, resp)" or "super.doPost(req, resp)" .
After i removed above super class calling from the doGet and doPost it worked fine.
Infact those super class calling codes were inserted by the Eclipse IDE template.
Are you sure that you actually override the doPost Method?
The signature looks like this:
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, java.io.IOException
but you specify it like this:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, IllegalStateException
I am not sure whether this is allowed:
Overriding Java interfaces with new checked exceptions
Try the following steps:
Add the #Override Annotation
Remove the throws IllegalStateException (shouldn't be a problem as it is a runtime exception)

Cannot access filter Servlet's session attribute from another normal Http Servlet

I have a problem here:
After i use a filter servlet to set session attribute, i try to retrieve the session attribute in another normal http servlet, but it looks getAttribute('system.userinfo') cannot retrieve anything. what's wrong with this? Thanks!
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
HttpServletResponse httpResp = (HttpServletResponse) response;
HttpSession session = httpReq.getSession();
httpReq.setCharacterEncoding("UTF-8");
UserDTO dto = new UserDTO();
session.setAttribute("system.userinfo", dto);
chain.doFilter(request, response);
}
public class FileUpload extends HttpServlet {
#SuppressWarnings("unchecked")
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
// cannot get anything here
UserDTO userinfo = (UserDTO)request.getSession(false).getAttribute("system.userinfo");
}
}
Both servlets are in same web application.
Seems like you are not getting the session in the servlet that you think got created in the Filter. In the filter you are using req.getSession() which is always creating a new session. In the servlet you are giving request.getSession(false), the container is supposed to return null if no session exists or return an existing session. Which servlet container are you using? If you are using an IDE, can you put a debug point and compare the session IDs to confirm they are the same? Also, is your UserDTO serializable?

Categories