Search code examples
javajspjakarta-eeservletsuser-management

How can I handle/restrict user-access to servlets & jsp's?


I'm currently writing a little dynamic web-application in Java. The application is supposed to be an event-platform where you can create a user-account, log in, and then you can see all open events (in a later iteration, users can create/participate in those events).

Right now, the structure of the web-app could be (simplified) described like this:

Register-Servlet -> Register.jsp
        |
        V
Login-Servlet -> Login.jsp
        |
        V
Main-page-Servlet -> Main.jsp

So right now, a user could go to Login.jsp, his login-information would be sent to the Login-Servlet, which would validate it and then send it to the Main-Page-Servlet. The Main-Page-Servlet then (after validating login again) gets all current events from a database, attaches it to the request, and forwards it to the Main.jsp, which displays it for the user to see.

Now, if a user wants to access the Main.jsp directly (without coming from the Main-Page-Servlet), it obviously can not display the available events. The workaround I'm using currently is doing a null-check to see if the events are there, and if not, redirect to the Main-Page-Servlet.

It bothers me to solve my problem like that, as I don't think that's the best practice and I think it will just create a lot of other problems the bigger my application gets.

My first thought about this was, that it might be useful if I could simply "hide" all .jsp's from the user, so the user would be landing on servlets only and could not access the .jsp's in a different way.

Is there a way to do that? Or, if not, what would be the best practice solution if I would be writing a professional enterprise-level application?


Solution

  • This can be handled in a Filter and there are great explanation and example in StackOverflow Servlet-Filter wiki.

    Adapting the code there for your problem (note the addition and usage of the needsAuthentication method):

    @WebFilter("/*")
    public class LoginFilter implements Filter {
        @Override
        public void init(FilterConfig config)
            throws ServletException {
            // If you have any <init-param> in web.xml, then you could get them
            // here by config.getInitParameter("name") and assign it as field.
        }
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
            HttpSession session = request.getSession(false);
    
            String requestPath = httpServletRequest.getRequestURI();
    
            if (needsAuthentication(requestPath) ||
                session == null ||
                session.getAttribute("user") == null) { // change "user" for the session attribute you have defined
    
                response.sendRedirect(request.getContextPath() + "/login"); // No logged-in user found, so redirect to login page.
            } else {
                chain.doFilter(req, res); // Logged-in user found, so just continue request.
            }
        }
    
        @Override
        public void destroy() {
            // If you have assigned any expensive resources as field of
            // this Filter class, then you could clean/close them here.
        }
    
        //basic validation of pages that do not require authentication
        private boolean needsAuthentication(String url) {
            String[] validNonAuthenticationUrls =
                { "Login.jsp", "Register.jsp" };
            for(String validUrl : validNonAuthenticationUrls) {
                if (url.endsWith(validUrl)) {
                    return false;
                }
            }
            return true;
        }
    }
    

    I would recommend to move all the pages that require authentication inside a folder like app and then change the web filter to

    @WebFilter("/app/*")
    

    In this way, you can remove the needsAuthentication method from the filter.