Статьи

Защита приложения JSF с помощью безопасности Java EE и JBoss AS 7.x

Общим требованием для корпоративных приложений является защита всех страниц JSF за страницей входа. Иногда вы даже хотите иметь защищенные области внутри приложения, которые доступны только пользователям, которым принадлежит определенная роль. Стандарты Java EE включают все средства, необходимые для реализации веб-приложения, защищенного некоторыми ограничениями безопасности. В этой записи блога мы хотим разработать простое приложение, которое демонстрирует использование этих средств и показывает, как вы можете создать полное приложение JSF для двух разных ролей. На первый взгляд, решение может показаться прямым, есть несколько подводных камней, на которые следует обратить внимание.

Первое, о чем мы должны позаботиться, это расположение папок нашего приложения. У нас есть три разных типа страниц:

  • Страница входа и страница ошибок для входа должны быть доступны всем пользователям.
  • У нас есть домашняя страница, которая должна быть доступна только для аутентифицированных пользователей.
  • У нас есть защищенная страница, которая должна быть видна только пользователям роли защищенной роли.

Таким образом, эти три типа страниц помещаются в три разные папки: корневая папка src / main / webapp, папка src / main / webapp / pages и защищенная страница находятся в src / main / webapp / pages / protected:

безопасность веб-ида

Файл web.xml — это место, чтобы определить, какие роли мы хотим использовать и как сопоставить доступность некоторого шаблона URL с этими ролями:

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
<security-constraint>
    <web-resource-collection>
        <web-resource-name>pages</web-resource-name>
        <url-pattern>/pages/*</url-pattern>
        <http-method>PUT</http-method>
        <http-method>DELETE</http-method>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
        <role-name>security-role</role-name>
        <role-name>protected-role</role-name>
    </auth-constraint>
</security-constraint>
<security-constraint>
    <web-resource-collection>
        <web-resource-name>protected</web-resource-name>
        <url-pattern>/pages/protected/*</url-pattern>
        <http-method>PUT</http-method>
        <http-method>DELETE</http-method>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
        <role-name>protected-role</role-name>
    </auth-constraint>
</security-constraint>
 
<security-role>
    <role-name>security-role</role-name>
</security-role>
<security-role>
    <role-name>protected-role</role-name>
</security-role>

Как видите, мы определяем две роли: роль безопасности и защищенная роль. URL-адреса, соответствующие шаблону / pages / *, доступны только тем пользователям, которым принадлежат роли security-protected и protected-role, тогда как страницы в / pages / protected / * доступны только пользователям с ролью защищенной роли.

Еще один момент, на который вы можете наткнуться — это страница приветствия. На первый взгляд, вы хотели бы указать страницу входа в качестве страницы приветствия. Но это не работает, так как модуль входа в контейнер сервлета автоматически перенаправляет все несанкционированные доступы на страницу входа. Поэтому мы указываем домашнюю страницу нашего приложения в качестве страницы приветствия. Это уже защищенная страница, но пользователь будет перенаправлен на страницу входа автоматически, когда он вызовет ее URL-адрес напрямую.

1
2
3
<welcome-file-list>
    <welcome-file>pages/home.xhtml</welcome-file>
</welcome-file-list>

Теперь мы почти закончили со страницей web.xml. Все, что нам нужно сделать, это определить метод аутентификации, а также страницу входа в систему и страницу ошибки, которая отображается в случае, если пользователь вводит неверные учетные данные. Следует обратить внимание на то, что на обеих страницах нет защищенных URL-адресов (например, файлов CSS или JavaScript), в противном случае даже доступ к этим двум страницам запрещен, и пользователь получает страницу с сообщениями об ошибках сервера приложений.

1
2
3
4
5
6
7
<login-config>
    <auth-method>FORM</auth-method>
    <form-login-config>
        <form-login-page>/login.xhtml</form-login-page>
        <form-error-page>/error.xhtml</form-error-page>
    </form-login-config>
</login-config>

Поскольку мы собираемся развернуть приложение на сервере приложений JBoss, мы предоставляем файл с именем jboss-web.xml, который соединяет наше приложение с доменом безопасности:

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
    <security-domain>java:/jaas/other</security-domain>
</jboss-web>

«Другой» домен безопасности настраивается внутри standalone.xml. Конфигурация по умолчанию требует, чтобы пользователь передавал модуль входа в систему «RealmUsersRoles», который получает свои определения пользователя и роли из двух файлов application-users.properties и application-roles.properties внутри папки конфигурации. Вы можете использовать предоставленный скрипт add-user для добавления нового пользователя в эту область:

01
02
03
04
05
06
07
08
09
10
11
12
13
What type of user do you wish to add?
 a) Management User (mgmt-users.properties)
 b) Application User (application-users.properties)
(a): b
 
Enter the details of the new user to add.
Realm (ApplicationRealm) :
Username : bart
Password :
Re-enter Password :
What roles do you want this user to belong to? (Please enter a comma separated list, or leave blank for none) : security-role,protected-role
About to add user 'bart' for realm 'ApplicationRealm'
Is this correct yes/no? yes

Здесь важно выбрать правильную область (ApplicationRealm), поскольку эта область настроена по умолчанию в standalone.xml для «другого» модуля входа в систему. Это также место, где вы предоставляете роли, которые пользователь имеет в виде списка через запятую.

01
02
03
04
05
06
07
08
09
10
11
<form method="POST" action="j_security_check" id="">
    <h:panelGrid id="panel" columns="2" border="1" cellpadding="4" cellspacing="4">
        <h:outputLabel for="j_username" value="Username:" />
        <input type="text" name="j_username"/>
        <h:outputLabel for="j_password" value="Password:" />
        <input type="password" name="j_password"/>
        <h:panelGroup>
            <input type="submit" value="Login"/>
        </h:panelGroup>
    </h:panelGrid>
</form>

Следующим шагом является реализация простой формы входа в систему, которая передает свои данные в модуль входа в систему. Обратите внимание на идентификаторы полей ввода, а также действие формы. Таким образом, форма отправляется в модуль входа в систему, который извлекает введенные имя пользователя и пароль из запроса. Разработчики JSF могут задаться вопросом, почему мы используем стандартную форму HTML вместо элемента. Причина этого заключается в том, что элементы формы JSF охватывают свое собственное пространство имен, и поэтому идентификаторы полей ввода имеют префикс с идентификатором формы (и этот идентификатор формы не может быть пустым).

Если пользователь прошел форму входа, мы представляем ему домашнюю страницу. Но ссылка на защищенную страницу должна быть доступна только пользователям, которым принадлежит защищенная роль. Это может быть выполнено с помощью следующего предоставленного условия:

1
<h:link value="Protected page" outcome="protected/protected" rendered="#{facesContext.externalContext.isUserInRole('protected-role')}"/>

И последнее, но не менее важное: нам нужна функция выхода из системы. Для этого случая мы реализуем простой компонент поддержки, такой как следующий, который делает недействительным сеанс пользователя и перенаправляет его обратно на страницу входа:

1
2
3
4
5
6
7
8
@Named(value = "login")
public class Login {
 
    public String logout() {
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
        return "/login";
    }
}
  • Как обычно, полный исходный код можно найти на github .