Одна из самых популярных публикаций в моем блоге — это краткое руководство по JDBC Security Realm и аутентификации на основе форм на GlassFish с Primefaces . После того, как я получил некоторые комментарии о том, что он больше не работает с последней версией GlassFish 3.1.2.2, я подумал, что, возможно, пришло время вернуться к ней и представить обновленную версию. Вот так:
подготовка
Как и в оригинальном уроке, я собираюсь положиться на некоторые вещи. Убедитесь, что установлена последняя бета-версия NetBeans 7.3 (которая включает GlassFish 3.1.2.2) и MySQL Community Server (5.5.x). Вы должны были убедиться, что все работает, и вы можете запустить GlassFish и MySQL Server также запущен.
Некоторые основы
Область проверки подлинности GlassFish, также называемая доменом политики безопасности или доменом безопасности, является областью, в которой сервер GlassFish определяет и применяет общую политику безопасности. Сервер GlassFish предварительно сконфигурирован с областями файлов, сертификатов и администрирования. Кроме того, вы можете настроить LDAP, JDBC, дайджест, Oracle Solaris или пользовательские области. Приложение может указать, какую область использовать в своем дескрипторе развертывания. Если вы хотите сохранить учетные данные пользователя для вашего приложения в базе данных, ваш первый выбор — это сфера JDBC.
Подготовьте базу данных

Запустите NetBeans и перейдите на вкладку «Службы». Щелкните правой кнопкой мыши узел «Базы данных» и выберите «Зарегистрировать MySQL Server». Заполните детали вашей установки и нажмите «ОК». Щелкните правой кнопкой мыши новый узел MySQL и выберите «подключиться». Теперь вы видите все уже имеющиеся базы данных. Снова щелкните правой кнопкой мыши и выберите «Создать базу данных». Введите ‘jdbcrealm’ в качестве нового имени базы данных. Примечание: мы не собираемся делать все это с отдельным пользователем базы данных. Это то, что настоятельно рекомендуется, но я использую пользователя root в этом примере. Если у вас есть пользователь, вы также можете предоставить полный доступ к нему здесь. Нажмите «ОК». Вы автоматически подключаетесь к вновь созданной базе данных. Разверните узел жирным шрифтом и щелкните правой кнопкой мыши «Таблицы». Выберите «Выполнить команду» или введите данные таблицы с помощью мастера.
|
01
02
03
04
05
06
07
08
09
10
11
|
CREATE TABLE USERS ( `USERID` VARCHAR(255) NOT NULL, `PASSWORD` VARCHAR(255) NOT NULL, PRIMARY KEY (`USERID`));CREATE TABLE USERS_GROUPS ( `GROUPID` VARCHAR(20) NOT NULL, `USERID` VARCHAR(255) NOT NULL, PRIMARY KEY (`GROUPID`)); |
Это все на данный момент с базой данных. Перейдите к следующему абзацу.
Дайте GlassFish знать о MySQL
Первое, что нужно сделать, это получить последнюю и лучшую версию MySQL Connector / J с веб-сайта MySQL, который на момент написания этой статьи составлял 5.1.22. Извлеките файл mysql-connector-java-5.1.22-bin.jar и поместите его в папку своего домена (например, glassfish \ domains \ domain1 \ lib). Готово. Теперь, наконец, пришло время создать проект.
Базовая настройка проекта
Запустите новый проект веб-приложения на основе Maven. Выберите «Новый проект»> «Maven»> «Веб-приложение» и нажмите «Далее». Теперь введите имя (например, secureapp) и все необходимые maven corinates и нажмите «Далее». Выберите настроенный сервер GlassFish 3+. Выберите Java EE 6 Web в качестве версии EE и нажмите «Готово». Теперь нам нужно добавить еще несколько настроек в наш домен GlassFish. Щелкните правой кнопкой мыши по вновь созданному проекту и выберите «Создать> Прочее> GlassFish> Пул соединений JDBC». Введите имя для нового пула соединений (например, SecurityConnectionPool) и под флажком «Извлечь из существующего соединения:» выберите свое зарегистрированное соединение MySQL. Нажмите «Далее. просмотрите свойства пула соединений и нажмите «Готово». Недавно созданная папка «Ресурсы сервера» теперь показывает ваш файл sun-resources.xml. Следуйте инструкциям и создайте «New> Other> GlassFish> JDBC Resource», указывающий созданный SecurityConnectionPool (например, jdbc / securityDatasource). Вы найдете настроенные вещи в «Other Sources / setup» в файле с именем glassfish-resources.xml , Он развертывается на вашем сервере вместе с вашим приложением. Так что вам не нужно заботиться о настройке всего с помощью консоли администратора GlassFish. Кроме того, нам все еще нужны Primefaces. Щелкните правой кнопкой мыши по своему проекту, выберите «Свойства», перейдите в категорию «Рамки» и добавьте «JavaServer Faces». Перейдите на вкладку «Компоненты» и выберите «PrimeFaces». Завершите, нажав «ОК». Вы можете проверить, сработало ли это, открыв pom.xml и проверив наличие зависимости Primefaces. 3.4 должен быть там. Не стесняйтесь менять версию на последнюю 3.4.2.
Конечная конфигурация GlassFish
Теперь пришло время запустить GlassFish и выполнить настройку области. В NetBeans снова перейдите на вкладку «Службы» и щелкните правой кнопкой мыши узел «GlassFish 3+». Выберите «Пуск» и посмотрите окно вывода для успешного запуска. Снова щелкните правой кнопкой мыши и выберите «View Domain Admin Console», после чего откроется браузер по умолчанию, указывающий на http: // localhost: 4848 /. Выберите «Конфигурации> Конфигурация сервера> Безопасность> Области» и нажмите «Создать…» в верхней части таблицы. Введите имя (например, JDBCRealm) и выберите com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm из раскрывающегося списка. Введите следующие значения в текстовые поля:
| JAAS | jdbcRealm |
| JNDI | JDBC / securityDatasource |
| Таблица пользователей | пользователи |
| Колонка имени пользователя | имя пользователя |
| Колонка паролей | пароль |
| Групповая таблица | группы |
| Колонка Имя группы | имя группы |
Оставьте все остальные значения по умолчанию / пробелы и выберите «ОК» в верхнем правом углу. Вам предоставляется необычное окно предупреждения JavaScript, в котором вам не нужно оставлять поле алгоритма дайджеста пустым. Я поставил ошибку об этом. По умолчанию это SHA-256. Что отличается от версий GlassFish до 3.1, которые использовали MD5 здесь. В более старой версии этого учебника алгоритм дайджеста вообще не использовался («нет»). Это должно было сделать вещи проще, но вообще не считается хорошей практикой. Итак, давайте придерживаться SHA-256 даже для разработки, пожалуйста.
Защитите ваше приложение
Закончено с настройкой вашей среды. Теперь нам нужно защитить приложение. Первая часть — подумать о ресурсах для защиты. Перейдите в папку «Веб-страницы» и создайте еще две папки. Один называется «admin», а другой — «users». Идея заключается в том, чтобы иметь две отдельные папки, к которым могут обращаться пользователи, принадлежащие к соответствующим группам. Теперь нам нужно создать несколько страниц. Откройте веб-страницы / index.xhtml и замените все, что находится между тегами h: body, следующим текстом:
|
1
2
3
4
5
6
|
<h:body> Select where you want to go: <br /> <h:link outcome='admin/index' value='To the admin section' /><br /> <h:link outcome='users/index' value='To the user section' /> </h:body> |
Теперь добавьте новый index.xhtml в пользовательские и административные папки. Заставьте их сделать что-то вроде этого:
|
1
2
3
4
5
|
<h:body> <h1>Hello Admin|User</h1> <br /> <h:link outcome='/index' value='Back to Homepage' /> </h:body> |
На логин.xhtml. Создайте его со следующим содержимым в корневом каталоге вашей веб-страницы.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
<?xml version='1.0' encoding='UTF-8' ?><!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'> <h:head> <title>Login Form</title> </h:head> <h:body> <p:panel header='Login From'> <form method='POST' action='j_security_check'> Username: <input type='text' name='j_username' /> Password: <input type='password' name='j_password' /> <br /> <input type='submit' value='Login' /> <input type='reset' value='Reset' /> </form> </p:panel> </h:body></html> |
Как видите, у вас есть базовый компонент Primefaces p: panel, имеющий простую HTML-форму, которая указывает на предопределенное действие j_security_check. Здесь происходит вся магия. Вы также должны включить два поля ввода для имени пользователя и пароля с предопределенными именами j_username и j_password. Теперь мы собираемся создать loginerror.xhtml, который отображается, если пользователь не ввел правильные учетные данные. (используйте тот же DOCTYPE и заголовок, что и в примере выше).
|
1
2
3
4
5
|
<h:body> <p:panel header='Login Error'> Sorry, you made an Error. Please try again: <a href='#{facesContext.externalContext.requestContextPath}/' >Login</a> </p:panel> </h:body> |
Единственное волшебство здесь — ссылка href якоря входа. Нам нужно получить правильный контекст запроса, и это можно сделать путем доступа к контексту лица. Если пользователь без соответствующих прав пытается получить доступ к папке, ему предоставляется страница ошибки 403 «Отказано в доступе». Если вы хотите настроить его, вам нужно добавить его и добавить следующие строки в ваш web.xml:
|
1
2
3
4
|
<error-page><error-code>403</error-code><location>/faces/403.xhtml</location></error-page> |
Это означает, что все не авторизованные запросы должны идти на страницу 403. Если у вас уже открыт web.xml, давайте начнем защищать ваше приложение. Нам нужно добавить ограничение безопасности для любого защищенного ресурса. Ограничения безопасности менее всего понятны веб-разработчикам, даже если они критически важны для безопасности веб-приложений Java EE. Указание комбинации шаблонов URL, методов HTTP, ролей и транспортных ограничений может быть затруднительным для программиста или администратора. Важно понимать, что любая комбинация, которая была предназначена для обеспечения безопасности, но не была указана с помощью ограничений безопасности, будет означать, что веб-контейнер будет разрешать эти запросы. Ограничения безопасности состоят из коллекций веб-ресурсов (шаблоны URL, методы HTTP), ограничения авторизации (имена ролей) и ограничений пользовательских данных (необходимость получения веб-запроса через защищенный транспорт, такой как TLS).
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
<security-constraint> <display-name>Admin Pages</display-name> <web-resource-collection> <web-resource-name>Protected Admin Area</web-resource-name> <description></description> <url-pattern>/faces/admin/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> <http-method>HEAD</http-method> <http-method>PUT</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> <http-method>DELETE</http-method> </web-resource-collection> <auth-constraint> <description/> <role-name>admin</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint> <security-constraint> <display-name>All Access</display-name> <web-resource-collection> <web-resource-name>None Protected User Area</web-resource-name> <description/> <url-pattern>/faces/users/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> <http-method>HEAD</http-method> <http-method>PUT</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> <http-method>DELETE</http-method> </web-resource-collection> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint> |
Если ограничения существуют, вы должны определить, каким образом контейнер должен бросить вызов пользователю. Веб-контейнер может аутентифицировать веб-клиента / пользователя с использованием схем аутентификации HTTP BASIC, HTTP DIGEST, HTTPS CLIENT или FORM. В этом случае мы используем аутентификацию на основе форм и определяем JDBCRealm
|
1
2
3
4
5
6
7
8
|
<login-config> <auth-method>FORM</auth-method> <realm-name>JDBCRealm</realm-name> <form-login-config> <form-login-page>/faces/login.xhtml</form-login-page> <form-error-page>/faces/loginerror.xhtml</form-error-page> </form-login-config> </login-config> |
Имя области должно быть именем, назначенным ранее области безопасности. Закройте файл web.xml и откройте файл sun-web.xml, чтобы сопоставить имена ролей приложений с реальными группами в базе данных. Эта абстракция кажется странной, но у нее есть несколько причин. Он был введен, чтобы иметь возможность сопоставления ролей приложений с различными именами групп на предприятиях. Я никогда не видел, чтобы это широко использовалось, но эта функция есть, и вы должны настроить ее. Другие серверы приложений предполагают, что если сопоставление отсутствует, имена ролей и имена групп совпадают. GlassFish так не считает. Поэтому вы должны поместить следующее в glassfish-web.xml. Вы можете создать его, щелкнув правой кнопкой мыши на папке WEB-INF вашего проекта, выбрав «New> Other> GlassFish> GlassFish Descriptor»
|
1
2
3
4
|
<security-role-mapping> <role-name>admin</role-name> <group-name>admin</group-name></security-role-mapping> |
Это было _основно_ … все, что вам нужно, на месте. Единственное, чего не хватает — это пользователей в базе данных. Он все еще пуст … Нам нужно добавить тестового пользователя:
Добавление тестового пользователя в базу данных
И снова мы начнем с щелчка правой кнопкой мыши по базе данных jdbcrealm на вкладке «Службы» в NetBeans. Выберите «Выполнить команду» и вставьте следующее:
|
1
2
|
INSERT INTO USERS VALUES ('admin', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918');INSERT INTO USERS_GROUPS VALUES ('admin', 'admin'); |
Вы можете войти через пользователя: admin и пароль: admin и получить доступ к защищенной области. Пример кода для генерации хеша может выглядеть так:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
try { MessageDigest md = MessageDigest.getInstance('SHA-256'); String text = 'admin'; md.update(text.getBytes('UTF-8')); // Change this to 'UTF-16' if needed byte[] digest = md.digest(); BigInteger bigInt = new BigInteger(1, digest); String output = bigInt.toString(16); System.out.println(output); } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) { Logger.getLogger(PasswordTest.class.getName()).log(Level.SEVERE, null, ex); } |
Ссылка: аутентификация JDBC Realm и Form-based с помощью GlassFish 3.1.2.2 и Primefaces 3.4 от нашего партнера JCG Маркуса Эйзела ( Markus Eisele) из блога Enterprise Software Development с Java .
