Рубрики
Java 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;
    }
}
Рубрики
Java UML

Про App Widgets в Android

На сегодняшний день уже опубликовано большое количество учебного материала, в котором подробно, по шагам описывается процесс создания простого App Widget в стиле "HelloWorld". Под App Widget-ами подразумеваются приложения, которые можно поместить на основной экран в ОС Android. Как правило это различные индикаторы (состояние батареи, яркость экрана, погода, твитты, пробки и т.д.). На мой взгляд лучшим руководством является статья, размещенная непосредственно на сайте гугл.

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

Почему виджеты.

Идея попробовать написать свои виджеты возникла у меня давно, но до реальных опытов руки все-таки не доходили. По большому счету, для меня основным мотивов их создания было и остается любопытство и желание сделать что-то забавное.
В итоге в декабре, перед новым годом все-таки удалось выкроить пару недель для плотной работы с ними.
Для пробы были сделаны и выложены на Маркете пара виджетов (бесплатных):

1. Рождественская ёлка, на игрушках которой загораются различные индикаторы (громкость, включен ли WiFi, заряд батареи и т.д.)

widget_tree
Сhristmas Tree Widget

2. Робот. Тоже пять-в-одном виджет - показывает уровень громкости, заряд батареи, включен ли Wi-Fi, 3G, вибро-режим.

widget_robot
Face Widget

Приложения сейчас не только бесплатные, но и без рекламы. Я просто еще не начинал разбираться с работой AdMob SDK. Возможно, если все-таки разберусь, напишу об этом позже.

Итак.

Принципиальные отличия в работе с виджетами и диаграмма классов.

Рубрики
Java UML

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

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


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

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

Рубрики
3. Инструментарий UML

UML в Javadoc через maven+umlgraph

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

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

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

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

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

Рубрики
2. Теория программирования UML

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

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

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

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

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

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

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

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

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

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

Рубрики
3. Инструментарий UML

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

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

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

UMLet - www.umlet.com

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

Рубрики
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
Рубрики
UML

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

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

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

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

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