JAAS Authentication Tutorial

Шаг 2. основные понятия

Мы будем заниматься именно безопасностью которую предоставляет нам сервер приложений. Для начала, стоит познакомиться с некоторыми понятиями.

Аутентификация – проверка на существование человека(зарегистрированного) в нашем приложении. Является ли он тем, за кого себя выдает?

Авторизация – проверка прав аутентифицированного пользователя выполнять конкретные действия.

Введение

Хотелось бы в данном обзоре обсудить такую тему, как безопасность веб-приложений.

В Java есть несколько технологий, которые обеспечивают безопасность:

Security developer’s guide

To authorize access to resources, applications first need to authenticate the source of the request. The JAAS framework defines the term subject to represent the source of a request. A subject may be any entity, such as a person or a service. Once the subject is authenticated, a javax.security.auth.Subject is populated with associated identities, or Principals. A Subject may have many Principals. For example, a person may have a name Principal (“John Doe”) and a SSN Principal (“123-45-6789”), which distinguish it from other subjects.

A Subject may also own security-related attributes, which are
referred to as credentials; see the section Credentials. Sensitive credentials that require special protection,
such as private cryptographic keys, are stored within a private credential
Set. Credentials intended to be shared, such as public key
certificates, are stored within a public credential Set. Different
permissions are required to access and modify the different credential
Sets.

Subjects are created using these constructors:

    public Subject();

    public Subject(boolean readOnly, Set principals,
                   Set pubCredentials, Set privCredentials);

The first constructor creates a Subject with empty (non-null) Sets of Principals and credentials. The second constructor creates a Subject with the specified Sets of Principals and credentials. It also has a boolean argument which can be used to make the Subject read-only. In a read-only Subject, the Principal and credential Sets are immutable.

Похожее:  Сбербанк бизнес онлайн личный кабинет - Вход в систему

An application writer does not have to instantiate a Subject. If the application instantiates a LoginContext and does not pass a Subject to the LoginContext constructor, the LoginContext instantiates a new empty Subject. See the LoginContext section.

If a Subject was not instantiated to be in a read-only state, it can be set read-only by calling the following method:

    public void setReadOnly();

A javax.security.auth.AuthPermission with target “setReadOnly” is required to invoke this method. Once in a read-only state, any attempt to add or remove Principals or credentials will result in an IllegalStateException being thrown. The following method may be called to test a Subject‘s read-only state:

    public boolean isReadOnly();

To retrieve the Principals associated with a Subject, two methods are available:

    public Set getPrincipals();
    public Set getPrincipals(Class c);

The first method returns all Principals contained in the Subject, while the second method only returns those Principals that are an instance of the specified Class c, or an instance of a subclass of Class c. An empty set will be returned if the Subject does not have any associated Principals.

To retrieve the public credentials associated with a Subject, these methods are available:

    public Set getPublicCredentials();
    public Set getPublicCredentials(Class c);

The behavior of these methods is similar to that for the getPrincipals methods, except in this case the public credentials are being obtained.

To access private credentials associated with a Subject, the following methods are available:

    public Set getPrivateCredentials();
    public Set getPrivateCredentials(Class c);

The behavior of these methods is similar to that for the getPrincipals and getPublicCredentials methods.

To modify or operate upon a Subject‘s PrincipalSet, public credential Set, or private credential Set, callers use the methods defined in the java.util.Set class. The following example demonstrates this:

    Subject subject;
    Principal principal;
    Object credential;

    . . .

    // add a Principal and credential to the Subject
    subject.getPrincipals().add(principal);
    subject.getPublicCredentials().add(credential);

Note: An AuthPermission with target “modifyPrincipals”, “modifyPublicCredentials”, or “modifyPrivateCredentials” is required to modify the respective Sets. Also note that only the sets returned via the getPrincipals(), getPublicCredentials(), and getPrivateCredentials() methods with no arguments are backed by the Subject‘s respective internal sets. Therefore any modification to the returned set affects the internal sets as well. The sets returned via the getPrincipals(Class c), getPublicCredentials(Class c), and getPrivateCredentials(Class c) methods are not backed by the Subject‘s respective internal sets. A new set is created and returned for each such method invocation. Modifications to these sets will not affect the Subject‘s internal sets.

In order to iterate through a Set of private credentials, you need a javax.security.auth.PrivateCredentialPermission to access each credential. See the PrivateCredentialPermission API documentation for further information.

A Subject may be associated with an
AccessControlContext (see the doAs and
doAsPrivileged method descriptions in the following sections). The
following method returns the Subject associated with the specified
AccessControlContext, or null if no
Subject is associated with the specified
AccessControlContext.

    public static Subject getSubject(final AccessControlContext acc);

An AuthPermission with target “getSubject” is required to call Subject.getSubject.

The Subject class also includes the following methods inherited from java.lang.Object.

    public boolean equals(Object o);
    public String toString();
    public int hashCode();

Аутентификация

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

Мы с Вами не зря переключились на Tomcat, т.к. Tomcat имеет хорошо описанную архитектуру (см. “

“). Из описания этой архитектуры видно, что Tomcat как веб-сервер представляет веб-приложение как некоторый контекст, который и называют ”

“. Этот контекст позволяет каждому веб-приложению иметь свои настройки, изолированные от других веб-приложений. Кроме того, веб-приложение может влиять на настройки этого контекста. Гибко и удобно. Для более глубокого понимания рекомендуется к прочтению статья “

” и раздел документации Tomcat ”

“.

Как выше было сказано, наше веб-приложение может влиять на Tomcat Context нашего приложения при помощи файла

/META-INF/context.xml

. И одной из очень важных настроек, на которую мы можем повлиять, является Security Realms.

Security Realms

— это некоторая “область безопасности”. Область, для которой указаны определенные настройки безопасности. Соответственно, используя Security Realm мы применяем настройки безопасности, определённые для этого Realm. Security Realms управляются контейнером, т.е. веб-сервером, а не нашим веб-приложением.

” описывает Realm как набор данных о пользователях и их ролях для выполнения аутентификации. Tomcat предоставляет набор различных реализаций Security Realm’ов, одним из которых является ”

“.

Разобравшись немного с терминологией, давайте опишем Tomcat Context в файле

/META-INF/context.xml

Веб-приложение

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

Подробнее про Gradle можно прочитать в кратком обзоре: “

” или в официальной документации ”

“.

Нам нужно выполнить инициализацию проекта (Initialization), а для этого в Gradle есть специальный плагин: ”

” (Init – сокращение от Initialization, легко запомнить).

Чтобы воспользоваться этим плагином, выполним в командной строке команду:

gradle init --type java-application

После успешного выполнения у нас появится Java проект.

Откроем теперь на редактирование билд скрипт нашего проекта. Билд скрипт — это файл с названием

build.gradle

, который описывает нюансы сборки (билда) приложения. Отсюда и название такое, билд скрипт. Можно сказать, что это скрипт сборки проекта.

Gradle — это такой универсальный инструмент, базовые возможности которого расширяются благодаря плагинам. Поэтому, в первую очередь обратим внимание на блок “plugins” (плагины):


plugins {
    id 'java'
    id 'application'
}

По умолчанию Gradle, в соответствии с указанным нами ”

--type java-application

“, выставил набор некоторых базовых плагинов (core plugins), то есть тех плагинов, которые входят в поставку самого Gradle. Если перейти на сайте

в раздел “Docs” (то есть документация), то слева в списке тем в разделе “Reference” мы видим раздел ”

“, т.е. раздел с описанием этих самых базовых плагинов. Давайте выберем именно те плагины, которые нам нужны, а не те, которые нам сгенерировал Gradle.

Согласно документации, ”

” обеспечивает базовые операции с Java кодом, такие как компиляция исходного кода. Так же, согласно документации, ”

” обеспечивает нас средствами для работы с “executable JVM application”, т.е. с java приложением, которые можно запустить как самостоятельное приложение (например, консольное приложение или приложение с собственным UI). Получается, что плагин “application” нам не нужен, т.к. нам не нужно самостоятельное приложение, нам нужно веб-приложение. Удалим его. А так же настройку “mainClassName”, которая известна только этому плагину.

Далее, в том же разделе “

“, где была приведена ссылка на документацию по Application Plugin, есть ссылка на Gradle War Plugin.

, как сказано в документации, предоставляет поддержку создания Java веб-приложений в формате war. В формате WAR означает, что вместо JAR архива будет создан WAR архив. Кажется, это то, что нам нужно. Кроме того, как сказано в документации, “The War plugin extends the Java plugin”. То есть мы можем заменить плагин java на плагин war. Следовательно, наш блок плагинов в итоге будет иметь следующий вид:


plugins {
    id 'war'
}

Так же в документации к “Gradle War Plugin” сказано, что плагин использует дополнительный “Project Layout”. Layout с английского переводится как расположение. То есть war plugin по умолчанию рассчитывает на существование некоторого расположение файлов, которые он будет использовать для своих задач. Использовать для хранения файлов веб-приложения он будет следующий каталог:

src/main/webapp

Поведения плагина описано так:

То есть плагин будет учитывать файлы из этого расположения при сборке WAR архива нашего веб-приложения.

Кроме того, в документации Gradle War Plugin’а сказано, что данный каталог будет “root of the archive”. И уже в нём мы можем создать каталог WEB-INF и добавить туда файл web.xml. Что это за файл такой?

web.xml

— это “Deployment Descriptor” или “описатель развёртывания”. Это такой файл, который описывает, как нужно настроить наше веб-приложение для работы. В этом файле указывается, какие запросы будет обрабатывать наше приложение, настройки безопасности и многое другое. По своей сути он чем-то похож на manifest файл из JAR файла (см. “

“). Manifest файл рассказывает, как работать с Java Application (т.е. с JAR архивом), а web.xml рассказывает, как работать с Java Web Application (т.е. с WAR архивом).

Само понятие “Deployment Descriptor” возникло не само по себе, а описано в документе “

“.

Любое Java веб-приложение зависит от этого “Servlet API”. Важно понимать, что это API — то есть это описание некоторого контракта взаимодействия. Веб-приложения — это не самостоятельные приложения. Они запускаются на веб-сервере, который обеспечивает сетевое взаимодействие с пользователями.

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

Кроме того, получается, что веб-приложения все разные, а передача данных одинакова. То есть миллиону программистов пришлось бы писать для одной и той же цели код снова и снова. Поэтому за часть взаимодействия с пользователем и за обмен данными отвечает веб-сервер, а за формирование этих данных отвечает веб-приложение и разработчик.

А чтобы связать эти две части, т.е. веб-сервер и веб-приложение, нужен контракт их взаимодействия, т.е. по каким правилам они это будут делать. Чтобы как-то описать контракт, как должно выглядеть взаимодействие между веб-приложением и веб-сервером и придуман Servlet API.

Интересно, что даже если вы используете фрэймворки вроде Spring, то “под капотом” всё равно работает Servlet API. То есть вы используете Spring, а Spring за Вас работает с Servlet API.

Получается, что наш проект веб-приложения должен зависеть (depends on)

от Servlet API. В этом случае Servlet API будет зависимостью (dependency). Как мы знаем, Gradle в том числе позволяет декларативным образом описывать зависимости проекта. А то, каким образом можно управлять зависимостями, описывают плагины.

Например, Java Gradle Plugin вводит способ управления зависимостями “testImplementation”, который говорит, что такая зависимость нужна только для тестов.

А вот Gradle War Plugin добавляет способ управления зависимостями “providedCompile”, который говорит, что такая зависимость не будет включена в WAR архив нашего веб-приложения.

Почему мы не включаем Servlet API в наш WAR архив? Потому что Servlet API будет предоставлен нашему веб-приложению самим веб-сервером.

providedCompile

.

Таким образом, блок зависимостей (dependencies) будет иметь следующий вид:


dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
    testImplementation 'junit:junit:4.12'
}


Итак, вернёмся к web.xml файлу. По умолчанию, Gradle не создаёт никакой Deployment Descriptor, поэтому нам нужно сделать это самостоятельно.

Создадим каталог

src/main/webapp/WEB-INF

, а в нём создадим XML файл с названием

web.xml

.

Теперь давайте откроем саму спецификацию “Java Servlet Specification” и главу ”

“.

Как сказано в “14.3 Deployment Descriptor”, XML документ Deployment Descriptor’а описан схемой

Шаг 1. типы реализации

У нас есть несколько вариантов безопасности, которую мы можем использовать. Начнем с того, что объяснит нам всю суть.

Ручная реализация

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

И если что-то пойдет не так, мы допустим ошибку в своем алгоритме, то приложение стает под угрозой перед злоумышленниками.

Реализация с помощью сервера приложений

Естественно безопасность разрабатывается очень часто и есть уже конкретные шаблонны, как именно ее организовывать.

Чтобы уйти от написания безопасности вручную, уменьшить вероятность взлома системы и ускорить разработку каждый может воспользоваться реализацией безопасности, которую предлагает нам сервер приложений. Это хорошо продуманная и гибкая система.

Шаг 3. настройка безопасности

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

Мы создали основную структуру. Пока что нету ничего кроме статического html. Сейчас все ссылки доступны с приложения и можно дойти к любому файлу. Есть специально создано 2 ресурса: public, secured. Первым делом, ограничим доступ ко всему что находиться внутри secured folder.

Пример файла web.xml:

Шаг 4. аутентификация

Добавим теперь нашему приложению возможность аутентификации, которая есть у нас по умолчанию. Методы аутентификации:

1. BASIC – Здесь используется стандартная форма ввода данных для аутентификации.

При доступе к закрытым ресурсам вы увидите окно, которое попросит вас ввести свои данные.

2.FORM – Здесь используем свою html форму. Делаем настройки в web.xml:

login-config – конфигурация аутентификации.

auth-method – каким методом проводить аутентификацию. Если выбираем FORM, то нужно ввести дополнительную информацию:

form-login-config – дополнительная информация при использовании своей формы

Шаг 5. настройка сервера приложений

Теперь, осталось настроить наш сервер приложений и связать его с нашим приложением. Для связки, в зависимости от сервера, в приложении используется специальный файл, который имеет название: *-web.xml.

JBoss, WildFly: jboss-web.xml

GlassFish: sun-web.xml

Создаем нужный нам файл в директории WEB-INF.

В данном случае мы будем использовать properties файлы у нас на сервере, что будут хранить данные о пользователях.

Пример файла jboss-web.xml:

Шаг 6. использование безопасности в сервлетах

Здесь очень кратко покажу базовые настройки для сервлетов. Для этого, создадим 2 сервлета в нашем проекте.

SecuredServlet1 – если посмотреть на адресс, защищен с помощью настроек в web.xml

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *