22

I've a filter used for the login. It performs a textual checking, on fields "Username" and "Password". If and only if the textual checking is correctly done the request goes to the Servlet. This latter performs the control that has to interact with the Database. Is this chain correct?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
paolo2988
  • 857
  • 3
  • 15
  • 31

1 Answers1

57

Preface: I gather you're using homegrown login instead of container managed login. For all ways, see How to handle authentication/authorization with users in a database?


The filter (the interceptor) shouldn't check the validity of the username/password combo. That's the responsibility of the servlet (the controller).

The filter should merely check if the user is logged-in or not (usually by just checking the presence of a session attribute) and then continue the request or block it by redirecting back to the login page.

@WebFilter("/*")
public class LoginFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {    
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        HttpSession session = request.getSession(false);
        String loginURI = request.getContextPath() + "/login";

        boolean loggedIn = session != null && session.getAttribute("user") != null;
        boolean loginRequest = request.getRequestURI().equals(loginURI);

        if (loggedIn || loginRequest) {
            chain.doFilter(request, response);
        } else {
            response.sendRedirect(loginURI);
        }
    }

    // ...
}

The servlet should collect the submitted data, find the associated User in database and if found then store it as a session attribute and then redirect to the home page, else redisplay the form with validation errors.

@WebServlet("/login")
public class LoginServlet extends HttpServlet {

    @EJB
    private UserService userService;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        Map<String, String> messages = new HashMap<String, String>();

        if (username == null || username.isEmpty()) {
            messages.put("username", "Please enter username");
        }

        if (password == null || password.isEmpty()) {
            messages.put("password", "Please enter password");
        }

        if (messages.isEmpty()) {
            User user = userService.find(username, password);

            if (user != null) {
                request.getSession().setAttribute("user", user);
                response.sendRedirect(request.getContextPath() + "/home");
                return;
            } else {
                messages.put("login", "Unknown login, please try again");
            }  
        }

        request.setAttribute("messages", messages);
        request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
    }

}

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 4
    +1. And for an even better user experience, if the intercepted request is a GET, cache it (or add it as a hidden field in the login page), and redirect to this page once the login is successful. – JB Nizet Nov 07 '12 at 19:52
  • 1
    A concrete example for that can be found here: http://stackoverflow.com/questions/6388014/redirecting-back-to-actual-page-what-they-were-viewing-before-login/6388597#6388597 – BalusC Nov 07 '12 at 19:56
  • 4
    You're welcome. Make sure that the filter isn't invoked on the login page itself! It may otherwise end up in an infinite loop. You'd need to either put the restricted pages in a separate folder (so that an URL pattern of `/somefolder/*` could be used instead), or to add an additional check on the request URL if the enduser isn't currently requesting the login page. – BalusC Nov 07 '12 at 20:06
  • could you explain that additional check for the login page, or to exclude js or css pages? – Vnge Mar 06 '14 at 21:34
  • I tried this but it keeps failing with below error `code` Caused by: java.lang.RuntimeException: Uncompilable source code - LoginFilter is not abstract and does not override abstract method destroy() in javax.servlet.Filter at LoginFilter.(LoginFilter.java:15) – user2854333 Feb 15 '17 at 12:52
  • @user2854333 Just fill out `// ...` with empty bodies in order to get it to compile. – BalusC Feb 15 '17 at 15:49
  • I tried filling that with public void destroy() { System.out.println("Do Nothing"); } but still fails – user2854333 Feb 15 '17 at 16:51
  • @user2854333 Did you read the new error message carefully? Rinse and repeat. – BalusC Feb 15 '17 at 17:50
  • I have tried your solution but the login action not working, it keeps redirect me to the login Page ? – Mohamed Nagy Nov 22 '17 at 10:17
  • @BalusC, can you explain , Make sure that the filter isn't invoked on the login page itself! It may otherwise end up in an infinite loop. I am on loop. – parlad Mar 02 '18 at 07:49
  • @parlad: just do the same as `loginURI` in above given filter example. – BalusC Mar 02 '18 at 07:50