Как выполнить проверку учетных данных пользователя j_security_check в Vaadin?

У меня есть старое веб-приложение, которое мне нужно перенести на Vaadin.

В старом приложении есть страница, которая запрашивает у пользователя логин и пароль.

Страница JSP выглядит так:

<?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 содержит следующие записи:

<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>

Это означает, что в случае неудачной аутентификации пользователь перенаправляется на страницу login_fail.jsp.

В файле context.xml я нашел следующую запись, из которой я пришел к выводу, что в Tomcat есть внутренний механизм, который считывает правильные логин и пароль из указанной базы данных и выполняет их проверку без какого-либо дополнительного кода.

<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>

В приложении Vaadin у меня есть пользовательский интерфейс с текстовыми полями и кнопкой.

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) {

    }
}

Когда пользователь нажимает кнопку (выполняется метод loginButtonPressed), я хочу

  1. проверьте учетные данные, введенные в текстовые поля userName и password,
  2. отобразить окно уведомления, если учетные данные неверны и
  3. открывает другой пользовательский интерфейс (просмотр), если они верны.

Как я могу сделать так, чтобы мое приложение Vaadin получило доступ к этому механизму Tomcat для проверки учетных данных (т.е. мое приложение Vaadin должно выполнять проверку учетных данных точно так же, как и старое приложение)?

Какой код позволяет мне проверить, действительны ли имя пользователя x и пароль y в соответствии с базой данных, указанной в context.xml?

Обновление 1 (05.11.2013): Я нашел описание того, как использовать аутентификацию на основе области здесь.

Но столкнулся с проблемой:

Сервлет приложения в примере расширяет com.vaadin.terminal.gwt.server.AbstractApplicationServlet, который недоступен в моем проекте Vaadin 7.

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

Обновление 2 (05.11.2013 15:53):

Я попытался реализовать аутентификацию как можно проще:

1) Используйте механизм аутентификации BASIC. 2) Настройте веб-приложение так, чтобы все страницы Vaadin требовали входа пользователя в систему.

Для этого я добавил в 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>

Но это не работает: диалоговое окно учетных данных отображается при доступе к приложению, но когда я ввожу правильные учетные данные и нажимаю «ОК», то же диалоговое окно появляется снова.


person Mentiflectax    schedule 01.11.2013    source источник
comment
Дмитрий, вам удалось решить вашу проблему?   -  person Renat Gilmanov    schedule 07.11.2013
comment
да. Сейчас я использую Apache Shiro. Это позволяет использовать данные из базы данных из области Apache Tomcat.   -  person Mentiflectax    schedule 08.11.2013


Ответы (2)


Похоже, вы все слишком усложнили. Пожалуйста, найдите ниже реальное решение. Он используется для приложения Vaadin и просто работает.

1. Вам нужна внешняя страница входа

Это самый простой способ. При желании вы можете легко стилизовать эту страницу, чтобы не мешать работе пользователей.

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

Как обычно, у вас будет следующее:

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

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

2. Я бы посоветовал создать страницу сбоя внешнего входа

По сути, ему просто нужно уведомить пользователя о том, что что-то не так. Он также может предложить, например, повторно войти в систему.

3. Настройте правильный метод аутентификации

<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. Не забудьте установить тайм-аут сеанса

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

5. Готово, просто проверьте

person Renat Gilmanov    schedule 06.11.2013

Рекомендую исключить аутентификацию от Vaadin. Сохраните страницу JSP в качестве формы аутентификации и используйте аутентификацию по умолчанию из спецификации сервлета.

Если в вашем коде есть программируемая аутентификация, это открывает дверь для ошибок. Более того, ваш код сложнее. Лучше использовать декларативную безопасность. В этом случае аутентификация отделена от приложения. Ваше приложение проще, безопасность более надежна.

Посетите веб-семинар http://www.youtube.com/watch?v=PNAo3ApWA-A Whole довольно интересен, но пример того, как сделать аутентификацию, начинается с 47 минут. Представлены оба решения: кастомный код и декларативная безопасность.

Посмотрев этот вебинар, я уверен, что декларативная безопасность - правильный выбор.

Декларативная безопасность требует некоторой корректировки в web.xml. По сравнению с чистым приложением Vaadin, не все запросы обрабатываются VaadinServlet: запросы аутентификации должны обрабатываться по умолчанию. В любом случае декларативная безопасность стоит усилий по настройке.

person michaldo    schedule 02.11.2013