How to perform j_security_check user credentials check in Vaadin?

Dmitrii Pisarenko picture Dmitrii Pisarenko · Nov 1, 2013 · Viewed 17.5k times · Source

I have an old web application which I need to migrate to Vaadin.

In the old app there is a page which asks the user for login and password.

The JSP page looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru">

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="utf-8"%>
[...]
<head>
<title>Some product</title>
    <link rel="stylesheet" type="text/css" href="<%=request.getContextPath()+"/css/styles.css"%>" />
</head>
<body>
<%@ include file="inc/main-navigation.jsp"%>
    <form action="j_security_check" method="post" style="margin:0px">
        [...]
        <input type="text" style="width:100%" name="j_username" />
        <input type="password" name="j_password" />
    </form>
</body>
</html>

web.xml contains following entries:

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>Some product</realm-name>
    <form-login-config>
        <form-login-page>/login.jsp</form-login-page>
        <form-error-page>/login_fail.jsp</form-error-page>
    </form-login-config>
</login-config>

This means that in case of unsuccessful authentication the user is directed to page login_fail.jsp.

In the context.xml file I found following entry from which I conclude that there is an internal mechanism in Tomcat which reads correct login and password from the specified database and performs their check without any additional code.

<Context path="/myapp">
    <Realm className="org.apache.catalina.realm.JDBCRealm" driverName="com.microsoft.sqlserver.jdbc.SQLServerDriver" 
        connectionURL="[...]"
        userTable="[...]" userNameCol="user_name" userCredCol="user_pass"
        userRoleTable="[...]" roleNameCol="role_name"/>
</Context>

In the Vaadin application I have a UI with both text fields and a button.

public class BookkeepingView extends VerticalLayout implements View {
    public BookkeepingView()
    {
        VerticalLayout contentPanel = new VerticalLayout();

        contentPanel.setStyleName("body");
        contentPanel.addComponent(getHeaderPanel());
        contentPanel.addComponent(getLoginPanel());
        contentPanel.addComponent(getFooterPanel());

        addComponent(contentPanel);
    }

    private Component getHeaderPanel() {
        return createCustomLayoutPanel("main-navigation");
    }

    private Component getFooterPanel() {
        return createCustomLayoutPanel("copyright");
    }

    private CustomLayout getLoginPanel() {
        Panel panel = new Panel();
        panel.setSizeUndefined();
        addComponent(panel);

        CustomLayout customLayoutPanel = new CustomLayout("login");

        final TextField userName = new TextField();
        customLayoutPanel.addComponent(userName, "j_username");

        final PasswordField password = new PasswordField();
        customLayoutPanel.addComponent(password, "j_password");

        final Button loginButton = new Button(I18n.l("bookkeeping_login_button"));
        customLayoutPanel.addComponent(loginButton, "login");

        loginButton.addClickListener(new Button.ClickListener() {
            @Override
            public void buttonClick(Button.ClickEvent clickEvent) {
                loginButtonPressed();
            }
        });

        return customLayoutPanel;
    }

    private void loginButtonPressed() {
        Notification.show("Login button pressed", Notification.Type.TRAY_NOTIFICATION);
    }

    private CustomLayout createCustomLayoutPanel(final String aHtmlFileName) {
        Panel panel = new Panel();
        panel.setSizeUndefined();
        addComponent(panel);

        CustomLayout customLayoutPanel = new CustomLayout(aHtmlFileName);
        return customLayoutPanel;
    }


    @Override
    public void enter(ViewChangeListener.ViewChangeEvent event) {

    }
}

When the user presses the button (method loginButtonPressed is executed), I want to

  1. check the credentials entered in text fields userName and password,
  2. display a notification box, if the credentials are wrong and
  3. opens another UI (view), if they are correct.

How can I make my Vaadin application access that Tomcat mechanism for checking the credentials (i. e. my Vaadin application should perform the credentials check in exactly the same way as the old app)?

What code allows me to check whether user name x and password y are valid according to database specified in context.xml?

Update 1 (05.11.2013): I found a description of how to use realm-based authentication here.

But I ran into a problem:

The application servlet in the example extends com.vaadin.terminal.gwt.server.AbstractApplicationServlet, which is not available in my Vaadin 7 project.

@WebServlet(urlPatterns={"/ui/*", "/VAADIN/*"})
public class DemoAppServlet extends AbstractApplicationServlet {

Update 2 (05.11.2013 15:53):

I tried to implement authentication in the simplest possible way:

1) Use BASIC authentication mechanism. 2) Configure the web app so that all Vaadin pages require the user to be logged in.

In order to achieve this, I added following fragments to web.xml:

<servlet-mapping>
    <servlet-name>VaadinDemoApp</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>
<security-constraint>
    <display-name>VaadinDemoApplicationConstraint</display-name>
    <web-resource-collection>
        <web-resource-name>Vaadin application</web-resource-name>
        <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
        <role-name>viewer</role-name>
    </auth-constraint>
</security-constraint>
<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>Protected page</realm-name>
</login-config>
<security-role>
    <role-name>admin</role-name>
</security-role>

<security-role>
    <role-name>viewer</role-name>
</security-role>

But it doesn't work: The credentials dialog box is displayed upon access to the application, but when I enter correct credentials and press "OK", the same dialog box appears again.

Answer

Renat Gilmanov picture Renat Gilmanov · Nov 6, 2013

It seems like you have overcomplicated everything. Please find below real-life solution. It is used for a Vaadin application and simply works.

1. You need external login page

This is the simplest way. If you want, you can easily style that page in order not to break user experience.

<link rel="stylesheet" type="text/css" href="VAADIN/themes/reindeer/styles.css">
...
<div class="v-app v-theme-reindeer">

As usual you will have the following:

<form id="login" method="post" action="j_security_check" >

 <input name="j_username" type="text">
 <input name="j_password" type="text">

2. I would suggest to have external login failed page

Basically it just needs to notify a user there was something wrong. It might also suggest, for example, to relogin.

3. Configure proper auth method

<login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
        <form-login-page>/VAADIN/loginPage.html</form-login-page>
        <form-error-page>/VAADIN/loginFailed.html</form-error-page>
    </form-login-config>
</login-config>

4. Do not forget to set session timeout

<session-config>
    <session-timeout>60</session-timeout>
</session-config>

5. You are done, just test