Метка: uml

  • HTTPS авторизация по сертификату

    Отправка простых HTTP запросов из Java уже хорошо описана в различных источниках, например в официальном туториале, поэтому пересказывать в очередной раз как работать с URL и выкладывать примеры кода не вижу смысла. В случае если приходится работать с защищенным HTTPS соединением, также особых осложнений у большинства программистов возникнуть не должно (возможно кроме случаев с самозаверенными сертификатами, но на эту тему также очень много учебного материала в интернете). Несколько больший интерес представляют собой случай, когда HTTPS подключение требует дополнительной авторизации по клиентскому сертификату. Вот об этом и пойдет речь в этой статье.

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

    Более распространен такой способ авторизации не в клиент-серверных решениях, а скорее в межсерверном взаимодействии. Например при решения задач в сфере B2B, когда необходимо отправлять запросы к различным серверам контрагентов и внешним системам. Отчасти это связанно с тем, что одним из популярных способов создания таких коммуникаций является использование веб-служб, что в свою очередь означает программную отправку HTTP запросов. Поскольку внутренняя реализации JAX-WS, базируется на использовании стандартных классов (URL, HttpURLConnection и т.д.), решив задачу в общем виде (отправка https запросов с авторизацией по сертификату) автоматически решается задача для клиента веб-службы (речь идет о JAX-WS, в Axis2 задачу решать можно немного другим способом).

    Самое простое что можно сделать для решения этой задачи — указать ключи и пароли в системных свойствах:

    -Djavax.net.ssl.keyStore=privateKey.jks
    -Djavax.net.ssl.keyStorePassword=myPrivateKeyPassword
    -Djavax.net.ssl.trustStore=truststore.jks
    -Djavax.net.ssl.trustStorePassword=myTrustStorePassword
    

    В данном случае в файл privateKey.jks вы можете положить свой приватный (закрытый) ключ, с помощью которого будет проходить аутентификация на сервере и (при необходимости) в файл truststore.jks положить сертификат сервера, к которому идет обращение.

    Хранилище ключей может иметь тип не только JKS (Java Key Store), но например быть в более распространенном формате PKCS12. В таком случае в настройках следует указать тип хранилища:

    -Djavax.net.ssl.keyStoreType=pkcs12
    -Djavax.net.ssl.keyStore=privateKey.p12                
    -Djavax.net.ssl.keyStorePassword=myPrivateKeyPassword
    

    Очень хорошо описаны отличия в использовании JKS и PKCS12 в статье Java 2-way TLS/SSL (Client Certificates) and PKCS12 vs JKS KeyStores.

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

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

    Общая последовательность шагов будет выглядеть следующим образом.
    1. Получить хранилище ключей.
    2. Создать объект класса менеджера ключей.
    3. Создать объект управляющий доверенными сертификатами.
    4. Произвести инициализацию ssl-контекста.

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

    Из диаграммы легко видеть как можно управлять поведением работы программы. Например, чтобы поменять логику выбора алиаса, можно сделать наследника класса менеджера ключей (KeyManager).
    Для этого вначале нужно получить хранилище ключей (KeyStore), а дальше сделав своего наследника, который будет работать с ключами, передать его в метод для инициализации SSL контекста. Самый простой способ сделать такого наследника — получить менеджера ключей по-умолчанию, а затем обернуть его методы таким образом, чтобы основные методы “проксировались” и в то же время можно было перекрыть нужный метод со специфической логикой (выбор алиас, отслеживание какой ключ/принципал используется и тд).

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

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

    
    /**
     *
     * @author Vit vit@programmisty.com
     */
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            SSLContext sc = SSLContext.getInstance("SSL");
    
            // Каким-то образом получили хранилище ключей. Это за рамками статьи.
            KeyStore keystore = ... ;
    
            String algorithm = KeyManagerFactory.getDefaultAlgorithm();
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm);
            // не забываем инициализировать менеджера ключей
            keyManagerFactory.init(keystore, password);
            // получаем список менеджером (при необходимости делаем наследника и/или оборачиваем keyManager)
            KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
            // если у нас свой список доверенных серверов
            TrustManager[] trustManagers = getTrustManagers("/trusted.jks", "trustedPassword".toCharArray());
            sc.init(keyManagers, trustManagers, null);
            // проставляем контекст
            SSLContext.setDefault(sc);
            // для проверки отправляем запрос
            String url = "https://my-secure-server.com";
            try (InputStream in = new URL(url).openStream()){
                System.out.println(IOUtils.toString(in));
            }
        }
    
        /**
         * если у нас какой-то свой файлик с доверенными сертификатами
         */
        private static TrustManager[] getTrustManagers(String path, char[] passwd) throws Exception {
            String algorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
            // Загружаем доверенных
            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
            // Из ресурсов, так проще. 
            InputStream keystoreStream = Demo.class.getResourceAsStream(path);
            keystore.load(keystoreStream, passwd);
            trustManagerFactory.init(keystore);
            // 
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            return trustManagers;
        }
    }
    
    
  • Липкий запуск сервисов в Android-e.

    В Android API есть такой абстрактный класс как Service. Он является наследником ContextWrapper-а, который в свою очередь является наследником Context-a. При некоторых допущениях можно относиться к сервисам как к “активити без UI” (хотя это не совсем правильно в деталях). Использовать сервис рекомендуется для задач не требующих прямого вмешательства пользователя.

    В документации особо акцентируется внимание на том, что Service не является ни процессом, ни ниткой. Если сервис должен делать какую-то “тяжелую” работу, то нужно самому выносить ее в отдельный thread, чтобы не получить ANR (Application Not Responding).

    Если делать свой thread лениво, то можно использовать готовый класс для асинхронной работы IntentService.

    Про него нужно знать следующее:

    • IntentService наследуется от Service.
    • Является абстрактным классом.
    • Нужно реализовать в нём метод onHandleIntent(Intent).
    • Обработка intent-ов идёт поочередно, но не в главном, а в своем выделенном thread-e.

    Диаграмма классов сервисов выглядит следующим образом: (далее…)

  • Android. Диаграмма классов: Context,Activity, Service

    Нарисованная “крупными мазками” UML диаграмма классов для наследников Context-а (из Android API) выглядит следующим образом:


    Важно! На этой диаграмме отображены только некоторые классы, которые являются (is) Context-ом. Методы также приведены в сокращенном количестве, чтобы упростить восприятие картины в целом.

    Сразу хочу заметить, что я не являюсь апологетом андроидного пути в архитектуре и дизайне, но поскольку многим программистам волей-неволей приходиться разрабатывать приложения под Android, то считаю нужным как минимум описать некоторые догмы этой архитектуры.
    (далее…)

  • UML в Javadoc через maven+umlgraph

    UPD из 2025. Сайт UMLGraph похоже перестал поддерживаться, попробуйте PlantUML

     

    Где-то год назад писал про различные UML-редакторы.

    К сожалению, ситуация толком не изменилась, пока действительно удобного на 100% для себя инструмента для создания UML-диаграмм не нашел.
    UMLet – хорошо спасает в 80% случаев для быстрого прототипирования, но иногда его не хватает.

    Сейчас речь пойдет не о нем, а о UMLGraph.
    Это инструмент, который позволяет осуществлять декларативный способ рисования uml-диаграмм. Другими словами UML диаграммы создаются в текстовом виде, а не рисуется мышкой. Некоторым программистам (в частности мне) работать с текстом несколько удобней, чем с мышкой и со множеством графических объектов.

    Возможностей у UMLGraph довольно много, но лично меня интересовали только следующие его возможности:

    1. Рисование только диаграмм классов. Использование umlgraph для создания диаграмм последовательности, развертывания и т.д. мне не интересно.
    2. Встраивание полученных диаграмм в JavaDoc.
    3. Связка с maven-ом.

    В результате некоторых манипуляций, можно получить следующий результат:
    (далее…)

  • Как рисовать UML диаграммы классов.

    Диаграмма классов.

    Архитекторы программного обеспечения разговаривают на языке UML. Это такая своеобразная программисткая латынь. Использовать UML напрямую для программирования  неудобно, зато многие его понимают и используют для выписывания рецептов описания архитектуры системы. Нарисовал диаграмму классов и стало понятней что к чему. Её поймет и дельфист и жаваист, и сишник и  питоньшик, и сишарпер и рубист (вобщем все кто изучал ООП).

    Центральное место в UML занимают диаграммы классов. Это букварь. В интернете можно найти огромное количество информации о диаграммах классов (структура, обозначение классов, интерфейсов, атрибутов, отношений и т.д. ), но при этом относительно мало информации о том, как осуществлять сам процесс проектирование. В этой связи хотел обратить внимание читателя на два особых момента.

    От общего к частному…

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

    Представим, что Вы не умеете хорошо рисовать, но вам очень хочется нарисовать например чью-то голову. Вы берете карандаш, кладете лист бумаги и начинаете рисовать его левый глаз. Потратив на этот несчастный левый глаз больше часа, Вы беретесь за нос, затем за правый глаз, ухо и только потом пытаетесь дорисовать все остальное. Вполне вероятно, что итог работы Вас опечалит (не советую показывать жертве результат работы) – глаз, нос, рот и уши будут не на своем месте. Конечно, бывают среди нас гении, у которых получится шедевр, но все-таки, те, кто занимается этим профессионально проповедуют другой подход – “От общего к частному, от частного к общему”. Вначале делается эскиз, “топорный” рисунок. Затем дальнейшая проработка. Например так:

    Точно так же я советую поступать в работе с диаграммами классов в UML. Мы создаем эскиз. Выделяем основные классы, определяем отношения между ними, затем прорабатываем мультипликаторы, поля, методы и их область видимости, сигнатуру вызовов, вспомогательные классы и так далее…

    Но начинать нужно с формирования набора базовых классов. Для этого нужно просто нарисовать прямоугольники и написать в них имена основных классов. Затем постепенно прорабатывать все остальное.

    Например так:

    (далее…)

  • Редактор UML. Альтернатива

    Кончено, для создания UML диаграмм можно использовать такие продукты как   Rational Software Modeler (Rational/IBM)или например Visual Paradigm for UML. Если открыть википедию, то в разделе про UML-редакторы можно найти около 30 ссылок на различные инструменты UML моделирования.

    И все-таки, не смотря на такой большой список, хотелось выделить несколько довольно интересных редакторов.

    UMLet – www.umlet.com

    Этот редактор обладает убийственной простотой.  В нём все просто и без излишеств – без закругленных уголков, выпендрежных иконок и градиентной заливки.  Аскетичный,  но при этом удобный. Он содержит только то, что нужно для работы с UML. Приблизительно так выглядят диаграммы, когда мы их рисуем на листе бумаги (только с ровными линиями). (далее…)

  • UML. Диаграммы классов. Отношения

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

    Обозначаются стрелками.

    Отношения на уровне классов.

    Generalization. Наследование.


    Самое обычное наследование: class A extends B {    }

    Implementation. Имплементация.


    Реализация интерфейса: class A implements I {   }

    Отношения на уровне объектов

    Association. Ассоциация.


    Семейство отношения между объектами классов.  “Студент” – “Преподаватель”, “Покупатель” – “Продавец” и т.д.  Может обозначаться вообще без стрелки.
    Агрегация и Композиция  –  подтипы ассоциации.

    Aggregation. Агрегация.


    Подтип ассоциации. Например один класс содержит (агрегируют) объекты другого класса.

    Composition. Композиция.


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

    Просто отношение

    Dependency. Зависимость.

    диаграмма классов зависимость

    Классы “каким либо образом” зависят друг от друга. Например, если у одного класса меняются методы, конструкторы или поля, и поэтому приходится переписывать другой класс, то значит они зависимы.  Одна из самых слабеньких связей.  Например объекты одного класса передаются как параметр в методы другого класса и т.д.

    Полезные ссылки

    1. Cтатья на  wikipedia. Рекомендую прочитать также эту же статью на английской википедии.
    2. Официальный сайт UML. Спецификация и т.д.
    3. uml2.ru – в целом по UML
  • Диаграмма последовательности (Sequence Diagram)

    Диаграмма последовательности (Sequence Diagram)

    Удобное средство для обозначения очередности следования друг за другом различных стимулов (сообщений), с помощью которых объекты взаимодействуют между собой.
    Например, когда нужно проработать буквально по шагам какой-то очень важный участок выполнения программы.
    Главный акцент – порядок и динамика поведения, т.е. как и в каком порядке происходят события.
    Отличие от диаграммы классов:
    Диаграмма классов дает статическую картинку, т.е. описание которое не меняется во время выполнения программы.
    Отличие от диаграммы коммуникаций (или как она раньше называлась colaboration):
    Диаграмма последовательности фокусирует наше внимание на очередности выполнения по времени, а диаграмма коммуникаций – на составляющих элементах.
    Обычно нормальные люди стараются описывать одной диаграммой только один определенный кейс (UseCase, вариант использования), например: “оставить коммент к сообщению в блоге”, “стать постоянным читателем” и т.д…
    Диаграммы последовательности,
    которые описывают всю систему сразу, представляют из себя монстра, пожирающего внимание, сознание, силы, время и мозг разработчика.

    Итак, предлагаю рассмотреть простенькую диаграмму последовательности.
    Возьмем банальный пример:

    диаграмма последовательности

    (далее…)

  • UML справочник

    мини-справочник для UML!