- PVSM.RU - https://www.pvsm.ru -

Единая авторизация (SSO) средствами JASIG CAS. Часть 2

Единая авторизация (SSO) средствами JASIG CAS. Часть 2
Приветствую, уважаемые хабро-читатели. Перед вами продолжение серии статей про JASIG CAS. В этой части я расскажу, как собрать артефакт CAS и начать с ним работать. Прежде, чем читать дальше, я надеюсь, вы прочитали первую часть [1].

Настроились, собрались… разобрались.

Как говорится, правильный настрой — половина дела. Для того, что бы правильно сконфигурировать и собрать свою версию CAS, вы должны хорошо представлять себе, как работают Spring и Maven.
Если вы хотите познать дао настройки CAS от начала до конца, то вам вот сюда [2]. Я же расскажу про основные вещи, которые нужно знать, для того, что бы успешно запустить CAS в той среде, которую я описал в первой статье.
Для начала приведу список файлов, которые могут понадобиться для настройки. После этого, я остановлюсь подробнее на тех из них, которые считаю наиболее важными. Оригинал списка можно найти здесь [3].

Файлы конфигурации

  • WEB-INF/classes/log4j.xml. В этом файле находятся настройки логирования. При желании можно добавлять свои логеры, CAS не накладывает никаких ограничений.
  • WEB-INF/cas-servlet.xml. Это Spring MVC конфигурация сервлетов, обрабатывающих сервисные URL CAS-а
  • WEB-INF/cas.properties. Содержит URL-ы служебных сервисов, SQL диалект и вообще любые свойства какие придумаетe, можно складывать сюда.
  • WEB-INF/deployerConfigContext.xml. Почти все настройки сервиса находятся в этом файле.
  • WEB-INF/login-webflow.xml. Здесь, с помощью Spring Web Flow, настраивается поток авторизации.
  • WEB-INF/restlet-servlet.xml. Настройка RESTful APIs. По идее вам никогда не придется менять этот файл.
  • WEB-INF/classes/messages_*.properties. Это набор property файлов, используемые для локализации CAS.
  • WEB-INF/spring-configuration. В этой директории расположены настройки большинства компонентов. Многие из них можно смело переопределять.
Файлы конфигурации расположенные в WEB-INF/spring-configuration

  • applicationContext.xml. Содержит стандартные бины, которые обычно не нужно переопределять, такие как планировщик Autowiring-а и настройки задач Quartz.
  • ticketRegistry.xml. Реестр тикетов. Вам вряд ли захочется его менять.
  • argumentExtractorsConfiguration.xml. Отвечает за выбор протокола. По умолчанию CAS и SAML.
  • propertyFileConfigurer.xml. Указывает путь, откуда брать свойства. По умочанию из WEB-INF/cas.properties
  • securityContext.xml.
  • ticketExpirationPolicies.xml. Настройки связанные с созданием и валидацией тикетов.
  • ticketGrantingTicketCookieGenerator.xml. Описывает способ создания TGT. Скорее всего менять его не придется.

deploymentconfigContext.xml

И так, ключевым файлом настройки CAS является deployerConfigContext.xml. Это довольно объемный файл, поэтому я буду разбирать его частями сверху вниз.
В самом начале файла располагается список резолверов учетных данных. Резолверы отвечают за извлечение учетных данных из запроса авторизации, приходящей на сервер.

<property name="credentialsToPrincipalResolvers">
...
</property>

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

Дальше идет описание обработчиков, которые отвечают за то, как CAS будет использовать полученные учетные данные. Я использую их для поиска пользователя в LDAP

<property name="authenticationHandlers">
<list> 
<bean class="org.jasig.cas.adaptors.ldap.BindLdapAuthenticationHandler"
                      p:filter="userLogin =%u"
                      p:searchBase=""
		      p:timeout="${ldapReadConnectTimeout}"	
                      p:contextSource-ref="contextSource"/> 
</property>

ВАЖНО! В продуктовой среде обязательно удалите SimpleTestUsernamePasswordAuthenticationHandler.

ВАЖНО! Обратите внимание на параметр p:timeout="${ldapReadConnectTimeout}". Это таймаут чтения/соединения с LDAP. Без установки этого параметра стандартные значения не будут переписаны, не смотря, даже, на установленные параметры com.sun.jndi.ldap.connect.timeout com.sun.jndi.ldap.read.timeout, о которых речь пойдет чуть позже.

Следующий блок конфигураций связан с настройкой источника данных о пользователях. Я приведу пример конфигурации, когда пользователи хранятся в openLdap.
Есть два способа поиска пользователей в LDAP:

  • FastBindLdapAuthenticationHandler. Он наиболее быстрый, но подходит только в тех случаях, когда из учетных данных можно напрямую построить DN (distinguished name), т.е. uid= %u,ou=users,dc=domain. Где %u — это переданный логин.
  • BindLdapAuthenticationHandler. Он чуть медленнее, но позволяет больше свободы в выборе нужной записи. Работает он в 2 этапа — с начал происходит LDAP bind. Учетные для bind берутся из contextSource, о котором я расскажу чуть позже. 2-этап — LDAP search c применением фильтра.

ВАЖНО! При работе с LDAP, base, для операций search и bind используется по-разному. Для bind всегда нужно указывать полное имя записи, т.е., например cn=user,dc=sso,dc=ru, где base — dc=sso,dc=ru. Поэтому base в настройке contextSource всегда должен быть заполнен и userDN — это всегда полное имя пользователя, под которым авторизуется CAS в LDAP.

Во время поиска searchBase добавляется к фильтру поиска для ограничения области сканирования. Он вполне может быть пустым, в этом случае критерий поиска в директории определяется только фильтром.

Пример настройки contextSource.

<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="pooled" value="false"/>
<property name="urls">
<list>
<value>ldap://your.host.nameORip</value>
</list>
</property>
<property name="userDn" value="cn=user,dc=subdomain,dc=domain"/>
<property name="password" value="verybigsecret"/>
<property name="base" value="dc=subdomain,dc=domain"/>
<property name="baseEnvironmentProperties">
<map>
<entry>
<key>
<value>java.naming.security.authentication</value>
</key>
<value>simple</value>
</entry>
<entry key="com.sun.jndi.ldap.connect.timeout" value="5000"/>
<entry key="com.sun.jndi.ldap.read.timeout" value="5000"/>
</map>
</property>
</bean>

Параметры com.sun.jndi.ldap.(connect, read).timeout отвечают за таймауты при соединении и чтении данных из LDAP соответственно.

С userDetailsService проблем возникнуть не должно, поэтому перейдем сразу к последней важной настройке — хранилищу сервисов. Есть несколько способов хранить зарегистрированные сервисы. По умолчанию используется inMemory хранилище. Этот вариант не подходит для продуктовой среды и тех случаев, когда вы хотите разделять один список сервисов среди нескольких CAS серверов. Мы используем MySql, развернутый, на той же машине, что и сервер CAS. Если такой вариант приемлем для вас, вы можете просто скопировать эту конфигурацию и подставить свои логинпароль.

<bean id="serviceRegistryDao"
          class="org.jasig.cas.services.JpaServiceRegistryDaoImpl" 
          p:entityManagerFactory-ref="entityManagerFactory"/>

<bean id="entityManagerFactory"     class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true"/>
<property name="showSql" value="true"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="dataSource"
            class="org.apache.commons.dbcp.BasicDataSource"
            p:driverClassName="com.mysql.jdbc.Driver"
            p:url="jdbc:mysql://localhost:3306/cas?autoReconnect=true"
            p:username="root"
            p:password=""/>

ticketExpirationPolicies.xml

Это еще один важный файл настройки. В нем описаны expiration policies для ST и TGT. Вам может понадобиться увеличить значения параметров для ST, если вы собираетесь изменять протокол взаимодействия, наблюдаются серьезные сетевые задержки или сервера, участвующие в SSO, плохо синхронизированы по времени.

cas.properties

Стандартных свойств здесь не так много.
cas.securityContext.serviceProperties.service. Значение его может быть например https:// localhost:8443/cas/services/j_acegi_cas_security_check. Это URL сервлета, который производит проверки тикетов. У нас он выглядит вот так: ${service} т.к. эту и многие другие настройки мы предпочитаем выносить в свойства MAVEN профилей и подставлять при сборке. Как это делается, я расскажу чуть позже.
cas.securityContext.serviceProperties.adminRoles=ROLE_ADMIN. Это свойство содержит названия ролей пользователей из Spring Security. Пользователи с этими ролями имеют доступ к служебным интерфейсам CAS. Эта настройка непосредственно связана с userDetailsService из deployerConfigContext.xml.
cas.securityContext.casProcessingFilterEntryPoint.loginUrl. По этому пути расположен сервлет, который принимает учетные данные пользователя.
cas.securityContext.ticketValidator.casServerUrlPrefix. URL CAS.
host.name. Как я понял, этот параметр используется в основном как префикс в именах TGT и ST. Так что можете выбрать любое название на ваш вкус.
database.hibernate.dialect. Это свойство определяет SQL диалект Hibernate. В deployerConfigContext.xml мы выбрали MySQl для хранения сервисов, соответственно его значение должно быть установлено в org.hibernate.dialect.MySQLDialect.

CAS позволяет задавать разное оформление для своих web интерфейсов, с помощью CAS themes. Как это делается, я расскажу, наверно, в другой статье. Подробно о темах можно почитать вот здесь [4].

Сборка

Теперь, когда с настройками разобрались, можно попробовать собрать свой собственный артефакт. Собирать будем с помощью Maven war overlay [5]
Для начала опишем свойства, которые нам понадобятся позже

<properties>
<spring.version>3.0.4.RELEASE</spring.version>
<spring.security.version>3.0.3.RELEASE</spring.security.version>
<cas.version>3.4.8</cas.version>
<compiler.version>2.0.2</compiler.version>
<source.version>1.6</source.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

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

Добавляем в проект зависимости в соответствии с настройками, сделанными в deployerConfigContext.xml

<!— Исходный проект   CAS  для которого будет применен OVERLAY -->
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-webapp</artifactId>
<version>${cas.version}</version>
<type>war</type>
<scope>runtime</scope></dependency>
<!— Зависимости для работы с базой  данных  -->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency> 
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.6.0.Final</version>
<exclusions>
<exclusion>
<artifactId>hibernate-core</artifactId>
<groupId>org.hibernate</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.14</version>
</dependency>
</dependencies>

<!--  Зависимости для работы с LDAP-->
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-support-ldap</artifactId>
<version>${cas.version}</version>
<exclusions>
<exclusion>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</exclusion>
<exclusion>
<artifactId>jaxb-impl</artifactId>
<groupId>com.sun.xml.bind</groupId>
</exclusion>
<exclusion>
<artifactId>spring-expression</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-expression</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>spring-expression</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
</exclusions>
</dependency>

Сборка с помощью overlay подходит, когда вам не нужны профили и фильтры ресурсов, но хочется использовать ресурсы другого проекта как базу для своей сборки.
Например, если нужно заменить deployerConfigContext.xml и cas.properties, в артефакте, это можно будет сделать вот так…

<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warName>cas</warName>
<overlays>
<overlay>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-webapp</artifactId>
<excludes>
<exclude>WEB-INF/deployerConfigContext.xml</exclude>
<exclude>WEB-INF/cas.properties</exclude>
</excludes>
</overlay>
</overlays>
</plugin>

Хотя этот способ довольно хорош я, все же, рекомендую воспользоваться профилями [6]. Они дают значительно больше свободы в конфигурировании приложения. С их помощью, например, очень просто организовать сборку артефактов для продуктовой и тестовой сред.
Теперь build тег будет выглядеть вот так

<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<warName>cas</warName> 
<webResources>
<resource>
<directory>src/main/config</directory>
<!-- override the destination directory for this resource -->
<targetPath>WEB-INF</targetPath>
<!-- enable filtering -->
<filtering>true</filtering>
</resource>
</webResources>
</configuration>
</plugin>

Теперь добавляем профили

<profiles>
<profile>
<id>test</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties> 
<service>
https://localhost:8443/cas/services/j_acegi_cas_security_check
</service> 
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<service>
https://auth.tcsbank.ru:8443/cas/services/j_acegi_cas_security_check
</service> 
</properties>
</profile>
</profiles>

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

Единая авторизация (SSO) средствами JASIG CAS. Часть 2
Теперь можно создать директорию /src/main/config и скопировать туда все ресурсы, для которых вы хотите проводить фильтрацию с помощью Maven. В итоге структура вашего проекта может выглядеть примерно так, как показано на картинке справа.
Осталось только собрать проект, например для продуктовой среды…

mvn -e clean compile package -P prod

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

Важно! Перед сборкой убедитесь, что для всех локалей, в которых предполагается использовать CAS, присутствуют все сообщения. Для этого нужно убедиться, что файл сообщений messages для нужных локалей содержат те же сообщения, что и файл для локали en. Если это не так, вам нужно скопировать recource Bundle в свой проект в директорию /webapp/WEB-INF/classes/ и вручную дополнить список сообщений. Иначе CAS не сможет отобразить ни один сервисный интерфейс, в котором отсутствуют нужные сообщения.

Взлетаем

Ну вот, CAS собран и запущен и от полноценного сервиса единой авторизации нас отделяет последний шаг — добавление доверенных сервисов. Подробную инструкцию как это сделать, можно узнать тут [7].

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

Для того, что бы добавить сервис в CAS нужно перейти на страницу управления сервисам по адресу https://${serverUrl}/services/add.html. Получить доступ на эту страницу, может только авторизованный пользователь. Роли для пользователей настраиваются в cas.properties и deployerConfigContext.xml.
URL сервисов поддерживают шаблонизацию в стиле ANT. Например, что бы добавить в доверенные все сервисы, расположенные на локальной машине, можно использовать вот такой паттерн — ht tp*://localhost:*/**.

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

Спасибо что дочитали до конца)

Автор: SergeyPopov


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/avtorizatsiya/10019

Ссылки в тексте:

[1] первую часть: http://habrahabr.ru/company/tcsbank/blog/142407/

[2] вот сюда: https://wiki.jasig.org/display/CASUM/Home

[3] здесь: https://wiki.jasig.org/display/CASUM/Best+Practice+-+Setting+Up+CAS+Locally+using+the+Maven2+WAR+Overlay+Method

[4] вот здесь: https://wiki.jasig.org/display/CASUM/Theme+Control

[5] Maven war overlay: http://maven.apache.org/plugins/maven-war-plugin/overlays.html

[6] профилями: http://maven.apache.org/guides/mini/guide-building-for-different-environments.html

[7] узнать тут: https://wiki.jasig.org/display/CASUM/Adding+a+New+Service