Это продолжение заметки про использование OpenShift в качестве java-хостинга.
В прошлый раз мы разобрались как создавать приложения в облаке OpenShift. В наше распоряжение предоставлен бесплатный хостинг с сервером JBoss AS 7.1 и репозиторием git. Теперь попробуем написать что-нибудь чуть сложнее, чем обычный «hello, world», и использующее возможности JBoss AS и средств разработки JBoss Tools.

Одна из распространенных задач: разрешить доступ к определенным ресурсам только авторизованным пользователям, с разделением в соответствии с присвоенными ролями. Предлагается сделать это с использованием встроенного в jboss логин-модуля, а именно реализацией org.jboss.security.auth.spi.DatabaseServerLoginModule. Как не трудно догадаться, в этом случае пользователи и их роли будут храниться в базе данных.
Схема данных достаточно проста: это таблица APP_USER (пользователи), APP_ROLE (справочник ролей) и APP_MEMBERSHIP (назначенные роли), через которую реализуется связь много-ко-многим между первыми двумя таблицами.

Создадим в web-консоли новое jbossas-7 приложение с картриджем mysql-5.1 и импортируем его в Eclipse. Следует переключиться в перспективу «Web». Сразу после импорта, скорее всего, раздел Java Resources будет помечен как содержащий ошибку, а окне Problems будет написана причина:

Развернем дерево проекта:
Как видно, тут уже есть папки для java классов и ресурсов, а также в папке webapp файлы index.html, пара jsp-файлов, каталог WEB-INF с дескрипторами. Файл health.jsp можно сразу же удалить (а также описание сервлета health из дескриптора web.xml), зачем он здесь — непонятно. Файл snoop.jsp еще может пригодиться, в нем выводится кое-какая статистика о нашем приложении.
В корне проекта лежит pom.xml с единственной зависимостью
Это дает нам доступ ко всем включенным в jboss модулям (ознакомиться со всем списком можно, развернув ветку Libraries — Maven Dependencies.
Теперь нам понадобится файл, который не был импортирован Eclipse. Он находится в каталоге проекта по адресу .openshift/config/standalone.xml, и, как видно из названия, описывает конфигурацию экземпляра сервера jboss. Откроем его тут же, в Eclipse (если приложение будет отлаживаться на локальном сервере jboss, придется подобные манипуляции выполнить с файлом в папке сервера standalone/configuration/standalone.xml).
Для работы с русскими символами в базе данных соединение должно осуществляться в кодировке UTF-8. Поэтому найдем источник данных (в данном случае MysqlDS) и добавим сведения о кодировке:
Теперь создадим домен безопасности, который назовем, например «app-auth». Необходимо найти подсистему «urn:jboss:domain:security:1.1» и добавить в нее описание нашего домена:
Назначение свойств dsJndiName, principalsQuery, rolesQuery, думаю, очевидно. Последние 2 свойства говорят о том, что в базе будут храниться хеши паролей. Если эти свойства убрать, то пароли должны будут сохраняться в открытом виде, что допустимо при отладке, но с реальными данными делать не стоит.
Добавим в проект JPA. Для этого откроем свойства проекта и найдем раздел Project Facets, в данном разделе надо поставить галочку напротив JPA. Будет автоматически создан файл persistence.xml. Далее можно настроить доступ к базе в этом файле, а можно передать настройку в hibernate.cfg.xml. Я предпочитаю второе, так как в этом случае под рукой оказывается удобный графический интерфейс, а также есть возможность сделать reverse engineering из существующей базы.
Для второго способа необходимо:
— в persistence.xml сослаться на hibernate.cfg.xml:
— в папку src/main/resources добавить файл hibernate.cfg.xml следующего содержания:
Обратите внимание на hibernate.hbm2ddl.auto: значение update позволяет автоматически обновлять схему данных, чтобы она соответствовала модели, и нам не придется писать ни строчки DDL для этой базы!
format_sql и show_sql могут пригодиться при отладке;
Закладка редактора «Session Factory» предоставляет еще кучу настроек, но в пока они не понадобятся.
На этом настройку можно считать завершенной.
Данные описываются 2 классами. Связь много-ко-многим описывается с обеих сторон множествами. Хозяином связи будет AppUser (AppRole редко изменяется, это скорее справочник, чем сущность).
Поскольку в MySql отсутствуют последовательности и автоинкремент, для генератора выбрана стратегия GenerationType.TABLE. Остальное, думаю, понятно из аннотаций.
Для пароля указана длина 30: этого должно хватить для SHA-1 дайджеста (20 байт) в кодировке base64;
Для поля enabled указан тип Boolean, не всякий сервер это поймет (например, в FirebirdSQL придется создать домен с таким именем), но MySql его интерпретирует без вопросов.
При желании в проект также можно добавить класс AppGen, который будут соответствовать таблице генераторов APP_GEN, для того, чтобы наше приложение могло работать с устаревшими (legacy) SQL серверами. Дело в том, что по умолчанию в таблице APP_GEN будет создано поле — первичный ключ GEN_NAME длиной 256 символов, что не всегда поддерживается, и эту длину можно уменьшить, явно указав в аннотации. По мне так достаточно и 30 символов (см.например длину названий последовательностей в Oracle).
Инициализацию приложения будет выполнять тот самый класс my.app.jaas.Initializer, который был ранее указан в web.xml
Как видно, реализован единственный метод-слушатель ServletContextListener.contextInitialized, в котором проверяются и при необходимости создаются роли, а также проверяется наличие хотя бы 1 администратора. При отсутствии администратора создается учетная запись admin.
Статический метод encode можно будет использовать в модуле управления пользователями.
Также нам пригодится еще 1 метод logout(), с очевидным назначением.
Работа с базой данных в данном случае ведется не чере�� JPA, а на уровень ниже — через hibernate API, в результате можно использовать замечательный интерфейс org.hibernate.Criteria и выполнить все действия без единой строчки на sql, hql или jpql.
Тут можно рисовать любую форму, единственное требование — на сервер должны сабмититься значения j_username и j_password. Поскольку в данном случае используются компоненты richfaces, то в код страницы автоматически включается jQuery, возможности которого и используются в скрипте для позиционирования контейнера login-container и автоматического выделения элемента с именем пользователя.
Итак, все готово для первого запуска. Далее помещаем любой контент в каталоги webapp/view, webapp/admin, коммитим изменения на сервер, и после запуска приложения убеждаемся, что доступ в эти каталоги возможен только после аутентификации и при наличии соответствующих ролей.
При старте приложения в базе данных будут автоматически созданы необходимые таблицы и записи, в этом можно убедиться установив картридж phpmyadmin, либо включив трассировку запросов в файле hibernate.cfg.xml:
На приведенном выше примере была рассмотрена разработка приложения с аутентификацией для OpenShift. Это же приложение можно скомпилировать и использовать на любом другом сервере JBoss AS 7.1 и с любым из поддерживаемых sql диалектов. Различие будет только в расположении файла настройки standalone.xml, и в необходимости установки нужного jdbc модуля.
При настройке подключения к источнику данных следует помнить о кодировке.
В рассмотренном шаблоне использовался минимум подгружаемых библиотек, что немаловажно для ограниченных ресурсов, предоставляемых OpenShift Express. В основном используются модули, уже включенные в дистрибутив JBoss, как результат — экономия дискового пространства и времени публикации приложения.
В прошлый раз мы разобрались как создавать приложения в облаке OpenShift. В наше распоряжение предоставлен бесплатный хостинг с сервером JBoss AS 7.1 и репозиторием git. Теперь попробуем написать что-нибудь чуть сложнее, чем обычный «hello, world», и использующее возможности JBoss AS и средств разработки JBoss Tools.

Одна из распространенных задач: разрешить доступ к определенным ресурсам только авторизованным пользователям, с разделением в соответствии с присвоенными ролями. Предлагается сделать это с использованием встроенного в jboss логин-модуля, а именно реализацией org.jboss.security.auth.spi.DatabaseServerLoginModule. Как не трудно догадаться, в этом случае пользователи и их роли будут храниться в базе данных.
Схема данных достаточно проста: это таблица APP_USER (пользователи), APP_ROLE (справочник ролей) и APP_MEMBERSHIP (назначенные роли), через которую реализуется связь много-ко-многим между первыми двумя таблицами.

Создадим в web-консоли новое jbossas-7 приложение с картриджем mysql-5.1 и импортируем его в Eclipse. Следует переключиться в перспективу «Web». Сразу после импорта, скорее всего, раздел Java Resources будет помечен как содержащий ошибку, а окне Problems будет написана причина:
Последуем данному совету: выделяем корень проекта, вызываем контекстное меню Maven -> Update Project Configuration, выполняем, и ошибка исчезнет.Project configuration is not up-to-date with pom.xml. Run project configuration update

Развернем дерево проекта:
Как видно, тут уже есть папки для java классов и ресурсов, а также в папке webapp файлы index.html, пара jsp-файлов, каталог WEB-INF с дескрипторами. Файл health.jsp можно сразу же удалить (а также описание сервлета health из дескриптора web.xml), зачем он здесь — непонятно. Файл snoop.jsp еще может пригодиться, в нем выводится кое-какая статистика о нашем приложении.
В корне проекта лежит pom.xml с единственной зависимостью
<dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-6.0</artifactId> <version>1.0.0.Final</version> <type>pom</type> <scope>provided</scope> </dependency>
Это дает нам доступ ко всем включенным в jboss модулям (ознакомиться со всем списком можно, развернув ветку Libraries — Maven Dependencies.
Настройка конфигурации сервера
Теперь нам понадобится файл, который не был импортирован Eclipse. Он находится в каталоге проекта по адресу .openshift/config/standalone.xml, и, как видно из названия, описывает конфигурацию экземпляра сервера jboss. Откроем его тут же, в Eclipse (если приложение будет отлаживаться на локальном сервере jboss, придется подобные манипуляции выполнить с файлом в папке сервера standalone/configuration/standalone.xml).
Настройка кодировки
Для работы с русскими символами в базе данных соединение должно осуществляться в кодировке UTF-8. Поэтому найдем источник данных (в данном случае MysqlDS) и добавим сведения о кодировке:
<connection-url>jdbc:mysql://${env.OPENSHIFT_DB_HOST}:${env.OPENSHIFT_DB_PORT}/${env.OPENSHIFT_GEAR_NAME}?characterEncoding=UTF-8</connection-url>
Настройка модуля аутентификации
Теперь создадим домен безопасности, который назовем, например «app-auth». Необходимо найти подсистему «urn:jboss:domain:security:1.1» и добавить в нее описание нашего домена:
<security-domain name="app-auth"> <authentication> <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required"> <module-option name="dsJndiName" value="java:jboss/datasources/MysqlDS"/> <module-option name="principalsQuery" value="select PWD from APP_USER where USER_NAME=? and ENABLED=1"/> <module-option name="rolesQuery" value="select r.ROLE_NAME, 'Roles' from APP_ROLE r, APP_MEMBERSHIP m, APP_USER u where r.ROLE_ID=m.ROLE_ID and m.USER_ID=u.USER_ID and u.USER_NAME=?"/> <module-option name="hashAlgorithm" value="SHA-1"/> <module-option name="hashEncoding" value="base64"/> </login-module> </authentication> </security-domain>
Назначение свойств dsJndiName, principalsQuery, rolesQuery, думаю, очевидно. Последние 2 свойства говорят о том, что в базе будут храниться хеши паролей. Если эти свойства убрать, то пароли должны будут сохраняться в открытом виде, что допустимо при отладке, но с реальными данными делать не стоит.
Настройка приложения: Faces, безопасность, инициализация
добавим в web.xml следующие строки:
<!-- JSF mapping --> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> <!-- security --> <login-config> <auth-method>FORM</auth-method> <realm-name>app-auth</realm-name> <form-login-config> <form-login-page>/login.xhtml</form-login-page> <form-error-page>/login.xhtml</form-error-page> </form-login-config> </login-config> <security-role> <role-name>Admin</role-name> </security-role> <security-role> <role-name>Manager</role-name> </security-role> <security-constraint> <web-resource-collection> <web-resource-name>Admin Part</web-resource-name> <url-pattern>/admin/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>Admin</role-name> </auth-constraint> </security-constraint> <security-constraint> <web-resource-collection> <web-resource-name>All Users</web-resource-name> <url-pattern>/view/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>*</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint> <!-- инициализация --> <listener> <listener-class>my.app.jaas.Initializer</listener-class> </listener>
- JSF mapping — будем использовать xhtml формат страниц
- Настройка безопасности: ссылаемся на серверный логин-модуль, настроенный ранее; аутентификация с использованием формы; далее определяем 2 роли и назначаем пути к защищаемым ресурсам
- Инициализация — определяем класс, код которого должен быть выполнен при старте приложения. Тут мы сможем создать необходимые записи в базе данных (при первом запуске в базе должен быть создан пользователь с ролью администратора)
Настройка Maven: дополнительные зависимости в pom.xml
откроем pom.xml и добавим зависимости:
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.0.1.Final</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.richfaces.core</groupId> <artifactId>richfaces-core-impl</artifactId> <version>4.2.2.Final</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.richfaces.ui</groupId> <artifactId>richfaces-components-ui</artifactId> <version>4.2.2.Final</version> <scope>runtime</scope> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.6</version> </dependency>
- hibernate — необязательная зависимость, просто немного читерства, а в принципе можно обойтись возможностями JPA
- richfaces — большой набор компонент, расширенная поддержка ajax, несколько готовых скинов, встроенная библиотека jQuery, короче, облегчение жизни при программировании клиентской части. Можно заменить на IceFaces, PrimeFaces или любую другую понравившуюся библиотеку.
- commons-codec — понадобится для кодирования хешей в base64
Настройка Java Persistence
Добавим в проект JPA. Для этого откроем свойства проекта и найдем раздел Project Facets, в данном разделе надо поставить галочку напротив JPA. Будет автоматически создан файл persistence.xml. Далее можно настроить доступ к базе в этом файле, а можно передать настройку в hibernate.cfg.xml. Я предпочитаю второе, так как в этом случае под рукой оказывается удобный графический интерфейс, а также есть возможность сделать reverse engineering из существующей базы.
Для второго способа необходимо:
— в persistence.xml сослаться на hibernate.cfg.xml:
persistence.xml
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="test"> <properties> <property name="hibernate.ejb.cfgfile" value="/hibernate.cfg.xml" /> </properties> </persistence-unit> </persistence>
— в папку src/main/resources добавить файл hibernate.cfg.xml следующего содержания:
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory name=""> <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property> <property name="hibernate.format_sql">true</property> <property name="hibernate.show_sql">false</property> <property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.connection.datasource">java:jboss/datasources/MysqlDS</property> </session-factory> </hibernate-configuration>
Обратите внимание на hibernate.hbm2ddl.auto: значение update позволяет автоматически обновлять схему данных, чтобы она соответствовала модели, и нам не придется писать ни строчки DDL для этой базы!
format_sql и show_sql могут пригодиться при отладке;
Закладка редактора «Session Factory» предоставляет еще кучу настроек, но в пока они не понадобятся.
На этом настройку можно считать завершенной.
Модель данных
Данные описываются 2 классами. Связь много-ко-многим описывается с обеих сторон множествами. Хозяином связи будет AppUser (AppRole редко изменяется, это скорее справочник, чем сущность).
Поскольку в MySql отсутствуют последовательности и автоинкремент, для генератора выбрана стратегия GenerationType.TABLE. Остальное, думаю, понятно из аннотаций.
AppUser.java
import java.util.*; import javax.persistence.*; @ Entity @ Table(name = "APP_USER", uniqueConstraints = @ UniqueConstraint(columnNames = "USER_NAME")) public class AppUser implements java.io.Serializable { private Long userId; private String userName; private String displayName; private String pwd; private Boolean enabled; private Set<AppRole> roles = new HashSet<AppRole>(0); @ TableGenerator( name = "UserIdGen", table = "APP_GEN", pkColumnName = "GEN_NAME", pkColumnValue = "USER_ID", valueColumnName = "GEN_VAL", allocationSize = 10) @ Id @ Column(name = "USER_ID", nullable = false) @ GeneratedValue(strategy=GenerationType.TABLE, generator="UserIdGen") public Long getUserId() { return this.userId; } public void setUserId(Long userId) { this.userId = userId; } @ Column(name = "USER_NAME", nullable = false, length = 30) public String getUserName() { return this.userName; } public void setUserName(String userName) { this.userName = userName; } @ Column(name = "DISPLAY_NAME", length = 250) public String getDisplayName() { return this.displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } @ Column(name = "PWD", length = 30) public String getPwd() { return this.pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @ Column(name = "ENABLED") public Boolean getEnabled() { return this.enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } @ ManyToMany(fetch = FetchType.LAZY, cascade=CascadeType.ALL) @ JoinTable( name = "APP_MEMBERSHIP", joinColumns = { @ JoinColumn(name = "USER_ID", nullable = false, updatable = false) }, inverseJoinColumns = { @ JoinColumn(name = "ROLE_ID", nullable = false, updatable = false) }) public Set<AppRole> getRoles() { return this.roles; } public void setRoles(Set<AppRole> roles) { this.roles = roles; } }
Для пароля указана длина 30: этого должно хватить для SHA-1 дайджеста (20 байт) в кодировке base64;
Для поля enabled указан тип Boolean, не всякий сервер это поймет (например, в FirebirdSQL придется создать домен с таким именем), но MySql его интерпретирует без вопросов.
AppRole.java
import java.util.*; import javax.persistence.*; @ Entity @ Table(name = "APP_ROLE", uniqueConstraints = @ UniqueConstraint(columnNames = "ROLE_NAME")) public class AppRole implements java.io.Serializable { private Long roleId; private String roleName; private String displayName; private Set<AppUser> users = new HashSet<AppUser>(0); @ TableGenerator( name = "RoleIdGen", table = "APP_GEN", pkColumnName = "GEN_NAME", pkColumnValue = "ROLE_ID", valueColumnName = "GEN_VAL", allocationSize = 10) @ Id @ Column(name = "ROLE_ID", nullable = false) @ GeneratedValue(strategy=GenerationType.TABLE, generator="RoleIdGen") public Long getRoleId() { return this.roleId; } public void setRoleId(Long roleId) { this.roleId = roleId; } @ Column(name = "ROLE_NAME", length = 30) public String getRoleName() { return this.roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } @ Column(name = "DISPLAY_NAME", length = 250) public String getDisplayName() { return this.displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } @ ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles") public Set<AppUser> getUsers() { return this.users; } public void setUsers(Set<AppUser> users) { this.users = users; } }
При желании в проект также можно добавить класс AppGen, который будут соответствовать таблице генераторов APP_GEN, для того, чтобы наше приложение могло работать с устаревшими (legacy) SQL серверами. Дело в том, что по умолчанию в таблице APP_GEN будет создано поле — первичный ключ GEN_NAME длиной 256 символов, что не всегда поддерживается, и эту длину можно уменьшить, явно указав в аннотации. По мне так достаточно и 30 символов (см.например длину названий последовательностей в Oracle).
Инициализация приложения
Инициализацию приложения будет выполнять тот самый класс my.app.jaas.Initializer, который был ранее указан в web.xml
Initializer.java
@ ManagedBean public class Initializer implements ServletContextListener { private static final Logger log = Logger.getLogger(Initializer.class); @Override public void contextDestroyed(ServletContextEvent event) {} @Override public void contextInitialized(ServletContextEvent event) { loadData(); } @ PersistenceContext EntityManager em; private AppRole checkRole(String roleName, String displayName, Session session) { AppRole role = (AppRole)session.createCriteria(AppRole.class) .add(Restrictions.eq("roleName", roleName)) .uniqueResult(); if (role == null) { role = new AppRole(); role.setRoleName(roleName); role.setDisplayName(displayName); session.save(role); } return role; } private void loadData() { Session session = (Session) em.getDelegate(); AppRole adminRole = checkRole("Admin", "Администраторы", session); checkRole("Manager", "Менеджеры", session); if (adminRole.getUsers().size()==0) { AppUser user = (AppUser)session.createCriteria(AppUser.class) .add(Restrictions.eq("userName", "admin")) .uniqueResult(); if(user==null) { user = new AppUser(); user.setUserName("admin"); user.setDisplayName("Администратор"); user.setPwd(encode("topsecret")); user.setEnabled(true); session.save(user); } adminRole.getUsers().add(user);//nothing user.getRoles().add(adminRole); session.save(adminRole); session.save(user); } session.flush(); session.close(); } public static String encode(String value) { //get the message digest try{ MessageDigest md = MessageDigest.getInstance("SHA"); //SHA-1 algorithm md.update(value.getBytes("UTF-8")); //byte-representation using UTF-8 encoding format byte raw[] = md.digest(); String hash = Base64.encodeBase64String(raw).trim(); return hash; } catch(Exception e) { log.error(e, e); } return value; } public String logout() { FacesContext ctx = FacesContext.getCurrentInstance(); HttpSession session = (HttpSession)ctx.getExternalContext().getSession(false); session.invalidate(); return("logout"); } }
Как видно, реализован единственный метод-слушатель ServletContextListener.contextInitialized, в котором проверяются и при необходимости создаются роли, а также проверяется наличие хотя бы 1 администратора. При отсутствии администратора создается учетная запись admin.
Статический метод encode можно будет использовать в модуле управления пользователями.
Также нам пригодится еще 1 метод logout(), с очевидным назначением.
Работа с базой данных в данном случае ведется не чере�� JPA, а на уровень ниже — через hibernate API, в результате можно использовать замечательный интерфейс org.hibernate.Criteria и выполнить все действия без единой строчки на sql, hql или jpql.
Форма аутентификации
login.xhtml
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:a4j="http://richfaces.org/a4j" xmlns:rich="http://richfaces.org/rich" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:h="http://java.sun.com/jsf/html"> <h:head> <title>Вход в систему</title> <h:outputStylesheet> div.login-container { width: 255px; position: relative; margin: 0 auto 0 auto; } </h:outputStylesheet> </h:head> <h:body> <div class="login-container" id="login_container"> <rich:panel> <f:facet name="header"> <h:outputText value="Вход в систему" /> </f:facet> <form method="post" action="j_security_check" name="loginform" id="loginForm" target="_parent"> <h:panelGrid columns="2" cellpadding="2" columnClasses="right,left" width="100%"> <h:outputLabel for="j_username" value="Логин:" /> <h:inputText style="width: 155px;" id="j_username" value="" /> <h:outputLabel for="j_password" value="Пароль:" /> <h:inputSecret style="width: 155px;" id="j_password" value="" /> <h:panelGroup /> <h:panelGroup /> <h:panelGroup /> <h:panelGroup> <h:commandButton name="login" id="login-submit" value="Вход" /> <h:outputText value=" " escape="false"/> <h:commandButton type="button" id="login-cancel" value="Отмена" /> </h:panelGroup> </h:panelGrid> </form> </rich:panel> </div> <h:outputScript> (function(){ jQuery("#login_container").offset({top:Math.max(0,(jQuery(window).height()/2)-150)}); var el = jQuery("#j_username").get(0);el.focus();el.select(); })(); </h:outputScript> </h:body> </html>
Тут можно рисовать любую форму, единственное требование — на сервер должны сабмититься значения j_username и j_password. Поскольку в данном случае используются компоненты richfaces, то в код страницы автоматически включается jQuery, возможности которого и используются в скрипте для позиционирования контейнера login-container и автоматического выделения элемента с именем пользователя.
Итак, все готово для первого запуска. Далее помещаем любой контент в каталоги webapp/view, webapp/admin, коммитим изменения на сервер, и после запуска приложения убеждаемся, что доступ в эти каталоги возможен только после аутентификации и при наличии соответствующих ролей.
При старте приложения в базе данных будут автоматически созданы необходимые таблицы и записи, в этом можно убедиться установив картридж phpmyadmin, либо включив трассировку запросов в файле hibernate.cfg.xml:
<property name="hibernate.show_sql">false</property>
Выводы
На приведенном выше примере была рассмотрена разработка приложения с аутентификацией для OpenShift. Это же приложение можно скомпилировать и использовать на любом другом сервере JBoss AS 7.1 и с любым из поддерживаемых sql диалектов. Различие будет только в расположении файла настройки standalone.xml, и в необходимости установки нужного jdbc модуля.
При настройке подключения к источнику данных следует помнить о кодировке.
В рассмотренном шаблоне использовался минимум подгружаемых библиотек, что немаловажно для ограниченных ресурсов, предоставляемых OpenShift Express. В основном используются модули, уже включенные в дистрибутив JBoss, как результат — экономия дискового пространства и времени публикации приложения.
