How to perform j_security_check user credentials check in Vaadin?

17,962

Solution 1

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

Solution 2

I recommend you to exclude authentication from Vaadin. Keep JSP page as authentication form and use default authentication from Servlet specification.

If you have programmable authentication in your code, it opens door for bugs. Moreover, your code is more complicated. Better use declarative security. In that case authentication is separated from application. Your application is simpler, security is more trusted.

Take a look on webinar http://www.youtube.com/watch?v=PNAo3ApWA-A Whole is quite interesting, but example how to make authentication starts from 47 minute. Both solutions are presented: custom code and declarative security.

After seeing that webinar I'm sure declarative security is correct choice.

Declarative security need some adjustment in web.xml. Comparing to pure Vaadin application, not all requests are handled by VaadinServlet: authentication requests must be handled by default. Anyway, declarative security is worth configuration effort.

Share:
17,962
Glory to Russia
Author by

Glory to Russia

Updated on June 04, 2022

Comments

  • Glory to Russia
    Glory to Russia almost 2 years

    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.

    • Renat Gilmanov
      Renat Gilmanov over 10 years
      Dmitri, did you manage to solve your problem?
    • Glory to Russia
      Glory to Russia over 10 years
      Yes. I'm using Apache Shiro now. It allows to use the data from the database from the Apache Tomcat realm.