Разработка распределенных приложений в Microsoft.NET Framework

         

Использование расширения WSE


Для расширения функциональности веб служб ASP.NET служит надстройка WSE. WSE 3.0 позволяет так же создавать безопасные службы без использования IIS, используя любое приложение в качестве носителя, но при этом выбор транспортного протокола ограничен протоколом TCP. Установка WSE приводит к созданию гибкой настраиваемой промежуточной среды для использования веб сервисов, что позволяет как использовать дополнительные стандарты, так и реализовывать нестандартные расширения благодаря SOAP фильтрам, обрабатывающих передаваемые между клиентом и сервером пакеты SOAP. Последовательность фильтров образует трубопровод (pipeline), рис. 7.3.


Рис. 7.3.  Архитектура WSE

Основное достоинство WSE заключается в отделении политики доступа (policy) к веб службе от самой службы. Политика обычно является набором требований и ограничений для клиента веб службы. Политика включает в себя те или иные расширения (assertions или extensions), а также ограничивает доступ идентифицированных пользователей к службе с помощью списка управления доступа. Каждое из расширений WSE может создавать один или два фильтра (для исходящих и входящих пакетов) как на клиенте, так и на сервере (при наличии сборки с расширением на обеих сторонах обмена).

Политика доступа к веб службе может изменяться администратором системы через конфигурационные файлы (рис. 7.4). Как на клиенте, так и на сервере политика хранится в отдельном файле (по умолчанию wse3policyCache.config), а в файле конфигурации клиента или службы (web.config) указывается ссылка на нее. Данные файлы можно редактировать при помощи утилиты WseConfigEditor3.exe или вручную.


Рис. 7.4.  Конфигурация WSE

Далее приведен пример файла конфигурации web.config при использовании WSE. Раздел <system.web><webServices><soapServerProtocolFactory> включает WSE в цепочку обработки сообщений SOAP. Раздел <microsoft.web.services3><diagnostics> позволят вести журнал всех принятых и отправленных пакетов, что может быть полезно на этапе тестирования.


<?xml version="1.0" encoding="utf-8"?> <configuration> <system.web> <webServices> <soapServerProtocolFactory type="Microsoft.Web.Services3.WseProtocolFactory, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </webServices> <compilation debug="false"> <assemblies> <add assembly="Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> </assemblies> </compilation> </system.web> <microsoft.web.services3> <diagnostics> <trace enabled="true" input="C:\Inetpub\logs\InputTrace.webinfo" output="C:\Inetpub\logs\OutputTrace.webinfo" /> </diagnostics> <policy fileName="wse3policyCache.config" /> </microsoft.web.services3> </configuration> Файл политики будет подробнее рассмотрен далее в примере создания собственных расширений и фильтров.


Менеджер пользовательских записей


В WSE уже входят расширения, позволяющие использовать в политике доступа к веб службе несколько видов аутентификации пользователей, а именно:

аутентификация Kerberos (в пределах домена Active Directory);аутентификация на основе сертификатов X.509;аутентификация на основе имени пользователя и пароля, используемая вместе с серверным сертификатом (для шифрования трафика) или без него (в этом случае рекомендуется шифрование передаваемой информации на уровне транспортного протокола TCP).

Тот или иной способ аутентификации можно установить в политике при помощи утилиты WseConfigEditor3.exe или прямым редактированием файла политики WSE.

По умолчанию WSE использует для проверки имени и пароля список пользователей Windows, что не всегда может быть удобно. Для ведения нестандартного списка пользователей веб службы можно установить свой менеджер пользовательских записей. Для реализации нестандартной проверки подлинности в WSE имеется механизм так называемых поставщиков токенов безопасности (Microsoft.Web.Services3.Design.TokenProvider). Такие поставщики не являются сами по себе расширениями и не имеют доступа к пакету SOAP, а используется другими расширениями WSE. К последним относятся, в частности, стандартные расширения UsernameOverTransportAssertion и UsernameForCertificateAssertion, использующие менеджер пользовательских записей в момент проверки подлинности имени и пароля пользователя.

Менеджеры описываются в файле web.config в разделе <microsoft.web.services3><security><securityTokenManager>. Каждый менеджер связывается с каким либо именем токена, указанным в атрибуте localName, например как указано ниже.

<microsoft.web.services3> <security> <securityTokenManager> <add localName="UsernameToken" type="Seva.WS.Users.UsersListManager, Seva.WS.UsersManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..." namespace="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" > <users file="C:\Inetpub\users.config"/> </add> </securityTokenManager> </security> <policy fileName="wse3policyCache.config" /> </microsoft.web.services3>

При обнаружении использующим аутентификацию расширением записи о токене в заголовке пакета SOAP (в разделе <wsse:Security>) оно использует связанный с данным токеном менеджер, вызывая его метод VerifyToken.
По умолчанию установлен менеджер UsernameTokenManager, связанный с записью в заголовке SOAP вида <wsse:UsernameToken>. Нестандартный менеджер токенов может быть либо унаследован от класса UsernameTokenManager, изменив его функциональность, либо реализован с чистого листа наследованием класса SecurityTokenManager.

К сожалению, при создании наследника класса UsernameTokenManager следует учитывать, что метод AuthenticateToken создаваемого менеджера должен возвращать тот же пароль, который передается в пакете SOAP. Однако хранение самих паролей в базе пользователей – решение, с точки зрения безопасности, неверное. Вместо самого пароля в базе следует хранить его образ (хеш), по которому невозможно восстановить сам пароль. Однако такая реализация метода AuthenticateToken имеет свои основания. Проблема в том, что пароль в заголовках пакете SOAP может передаваться как в открытом виде (при этом требуется шифрование пакета на основе, например, сертификатов X.509) , так и в виде своего образа (digest). Образ вычисляется как хеш от конкатенации некоторой случайной строки, указанной в пакете (nonce), времени создания пакета и самого пароля:



Password_Digest = Base64(SHA1( nonce + created + password)) где SHA1 – широко применяемая криптографическая функция вычисления хеша. Она обеспечивает разный результата для разных аргументов с высокой вероятностью и невозможность вычисления аргумента по результату хеш преобразования. Видно, что для проверки пароля пользователя в данном случае необходимо иметь доступ к самому паролю, а не к его образу. Наилучшим решением данной проблемы будет отказ от передачи образа пароля в пакете и использования защиты всего сообщения на основе сертификатов или защита канала передачи данных на основе SSL.

Предлагаемый далее менеджер паролей работает в обеих случаях – при использовании открытых паролей в сообщении он предполагает, что в базе пользователей хранятся хеши паролей (метод VerifyPlainTextPassword). Этот вариант будет задействован, в частности, при использовании стандартных расширений.


При использовании же хешированных паролей в открытом сообщении предполагается, что в базе пользовательских записей хранятся сами пароли. Этот вариант будет задействован при добавлении клиентом в пакет SOAP элемента <wsse:UsernameToken>, создаваемого классом UsernameToken с параметром PasswordOption.SendHashed. Вариант такого расширения для клиента веб службы будет описан далее.

// UsersManager.cs using System; using System.IO; using System.Reflection; using System.Text; using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; using System.Collections.Generic; using System.Security.Cryptography; using System.Security; using Microsoft.Web.Services3; using Microsoft.Web.Services3.Design; using Microsoft.Web.Services3.Security; using Microsoft.Web.Services3.Security.Tokens; Поскольку для использования в политике WSE сборку удобнее зарегистрировать в GAC, то следует указать номер версии сборки.

[assembly:AssemblyVersionAttribute("1.0.0.0")] namespace Seva.WS.Users { public class UsersListManager: UsernameTokenManager { private UsersList users; public UsersListManager() { users = new UsersList(); } Основной конструктор класса UsersListManager должен загрузить список пользователей из указанного в конфигурации файла. На практике в случае большого числа пользователей следует использовать СУБД и запрос к базе данных пользователей непосредственно в методе AuthenticateToken.

public UsersListManager(XmlNodeList configData): base(configData) { string fileName = configData[0].Attributes["file"].Value; users = UsersList.Load(fileName); } protected override string AuthenticateToken(UsernameToken token) { if (!users.Users.ContainsKey(token.Username)) return null; return users.Users[token.Username]; } Метод VerifyPlainTextPassword модифицирован для работы с образами паролей.

protected override void VerifyPlainTextPassword(UsernameToken token, string authenticatedPassword) { if (token==null) throw new ArgumentNullException("token is null"); String hashed = Utils.HashedPassword(token.Password); if (authenticatedPassword==null || authenticatedPassword=="" || hashed!=authenticatedPassword) throw new Exception("Passwords does not match "); } } Список пользователей хранится в объекте UsersList.


Поскольку обычные классы словарей не могут быть использованы классом форматирования XmlSerialiser, то список пользователей реализует интерфейс IXmlSerializable.

[XmlRoot("users")] public class UsersList: IXmlSerializable { private Dictionary<string, string> usersField; public Dictionary<string, string> Users { get {return usersField;} } public UsersList() { usersField = new Dictionary<string, string>(); } public void ReadXml(XmlReader reader) { reader.Read(); while (reader.NodeType != XmlNodeType.EndElement) { Users.Add(reader.GetAttribute("username"), reader.GetAttribute("password")); reader.Read(); } } public void WriteXml(XmlWriter writer) { foreach (string user in Users.Keys) { writer.WriteStartElement("user"); writer.WriteAttributeString("username", user); writer.WriteAttributeString("password", this.Users[user]); writer.WriteEndElement(); } } public XmlSchema GetSchema() { return null; } Листинг 7.1. Метод NewUser используется для добавления нового пользователя.

public void NewUser(string userName, string password, bool hashed) { if (Users.ContainsKey(userName)) { throw new ApplicationException("Duplicate username."); } if (hashed) { password = Utils.HashedPassword(password); } Users.Add(userName, password); } Методы Load и Save записывают список пользователей и их паролей (или их образов) в файл.

public static UsersList Load(string fileName) { XmlSerializer serializer = new XmlSerializer(typeof(UsersList)); using (StreamReader reader = new StreamReader(fileName)) { return (UsersList) serializer.Deserialize(reader); }; } public void Save(string fileName) { XmlSerializer serializer = new XmlSerializer(typeof(UsersList)); using (StreamWriter writer = new StreamWriter(fileName)) { serializer.Serialize(writer, this); }; } } Статический класс Utils облегчает вызов функции хеширования и преобразует ее результат в строку.

static class Utils { public static string HashedPassword(string password) { SHA1CryptoServiceProvider crypto = new SHA1CryptoServiceProvider(); byte[] inputBuffer = Encoding.Unicode.GetBytes(password); return Convert.ToBase64String(crypto.ComputeHash(inputBuffer)); } } Следующий make файл создает сборку расширения c менеджером пользователей и регистрирует ее в GAC после команды nmake &amp;&amp; nmake install.



all: Seva.WS.UsersManager.dll Seva.WS.UsersManager.dll: UsersManager.cs UsersManager.key csc /t:library /out:Seva.WS.UsersManager.dll /keyfile:TimeAssertion.key /r:Microsoft.Web.Services3.dll UsersManager.cs UsersManager.key: sn -k UsersManager.key install: gacutil -nologo -i Seva.WS.UsersManager.dll На стороне сервера в файле конфигурации (web.config) следует добавить информацию о менеджере учетных записей в элемент <microsoft.web.services3> <security>.

<?xml version="1.0" encoding="utf-8"?> <configuration> ... <microsoft.web.services3> <security> <securityTokenManager> <add type="Seva.WS.Users.UsersListManager, Seva.WS.UsersManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..." namespace= "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" localName="UsernameToken"> <users file="C:\Inetpub\users.config"/> </add> </securityTokenManager> </security> <policy fileName="wse3policyCache.config" /> </microsoft.web.services3> </configuration> Кроме процедуры установления идентичности пользователя, может существовать необходимость ограничить доступ к службе тех или иных пользователей. Для этого в разделе <policy><authorization> политики веб службы можно организовать список пользователей, используя элементы <allow> и <deny>.

Для посылки клиентом хеша пароля может использоваться описанное далее расширение UsernameClientAssertion. Оно может быть использовано для не представляющей критической важности веб службы, не использующей сертификаты X.509. Использование выданных или "самодельных" серверных сертификатов X.509 и открытых паролей в зашифрованном сообщении позволяет обеспечить гораздо большую степень безопасности.

[XmlRoot("user")] public struct UserCredential { [XmlAttribute("username")] public string Username; [XmlAttribute("password")] public string Password; } public class UsernameClientAssertion: SecurityPolicyAssertion { private UserCredential credential; public UsernameClientAssertion() { } Метод ReadXml считывает из файла параметры политики, состоящие из имени файла с именем пользователя и паролем.


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

public override void ReadXml(XmlReader reader, IDictionary<string, Type> extensions) { string fileName = reader.GetAttribute("file"); if (fileName == null) throw new Exception("Attribute 'file' not found in policy"); // Обязательная операция - переход к следующему тегу в файле политики reader.Read(); using (StreamReader stream = new StreamReader(fileName)) { XmlSerializer serializer = new XmlSerializer(typeof(UserCredential)); using (XmlTextReader xmlReader = new XmlTextReader(stream)) { credential = (UserCredential) serializer.Deserialize(xmlReader); } } } Метод CreateClientOutputFilter данного расширения создает фильтр SOAP, который будет добавлять информацию о пользователе в заголовок сообщения.

public override SoapFilter CreateClientOutputFilter( FilterCreationContext context) { return new UsernameClientFilter(this, credential, context); } public override SoapFilter CreateClientInputFilter( FilterCreationContext context) { return null; } public override SoapFilter CreateServiceInputFilter( FilterCreationContext context) { return null; } public override SoapFilter CreateServiceOutputFilter( FilterCreationContext context) { return null; } } Фильтр наследован от класса SendSecurityFilter и добавляет в сообщение токен с паролем в форме хеша в своем методе SecureMessage.

class UsernameClientFilter : SendSecurityFilter { private UserCredential credential; public UsernameClientFilter(UsernameClientAssertion assertion, UserCredential credential, FilterCreationContext filterContext) : base(assertion.ServiceActor, true, assertion.ClientActor) { this.credential = credential; } public override void SecureMessage(SoapEnvelope envelope, Security security) { UsernameToken userToken = new UsernameToken(credential.Username, credential.Password, PasswordOption.SendHashed); security.Tokens.Add(userToken); } } } Для составления файла с пользователями можно использовать следующую вспомогательную программу.



// AddUser.cs using System; using System.IO; using Seva.WS.Users; class MainApp { public static int Main(string[] Args) { if (Args.Length < 3) { Console.WriteLine( "usage: adduser <users' file> <username> <password> [--hashed]"); return 1; } UsersList users = new UsersList(); string fileName = Args[0]; if (File.Exists(fileName)) { users = UsersList.Load(fileName); } bool hashed = false; if (Args.Length==4) { hashed = Args[3]=="--hashed"; } users.NewUser(Args[1], Args[2], hashed); users.Save(fileName); return 0; } } Листинг 7.2. На стороне сервера можно воспользоваться стандартным расширением WSE UsernameOverTransportAssertion без шифрования сообщения, которое будет использовать описанный ранее специальный менеджер пользовательских записей, простейший вариант файла политики wse3policyCache.config указан ниже.

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy"> <extensions> <extension name="usernameOverTransportSecurity" type="Microsoft.Web.Services3.Design.UsernameOverTransportAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <extension name="requireActionHeader" type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </extensions> <policy name="ServicePolicy"> <usernameOverTransportSecurity /> <requireActionHeader /> </policy> </policies> Файл политики клиента при использовании расширения UsernameClientAssertion имеет следующий вид.

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy"> <extensions> <extension name="simpleUserName" type="Seva.WS.Users.UsernameClientAssertion, Seva.WS.UsersManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..."/> </extensions> <policy name="ClientPolicy"> <simpleUserName file="user.xml"/> </policy> </policies> Файл user.xml содержит имя пользователя и пароль клиента веб службы.

<user username="user2" password="222" /> При использовании шифрования сообщений, например, на основе сертификатов X.509 и расширения UsernameForCertificateAssertion использовать описанное расширение клиента UsernameClientAssertion не следует, поскольку при шифровании всего сообщения нет необходимости посылать образ пароля вместо него самого. Поэтому можно использовать расширение стандартное UsernameForCertificateAssertion для клиента и сервера, которое будет использовать созданный менеджер пользователей. Наиболее простой способ конфигурирования политик в таком случае заключается в использование утилиты WseConfigEditor3.exe.


Описание веб службы


При использовании ASP.NET в директории IIS (по умолчанию это C:\Inetpub\wwwroot\) должен быть создан файл с расширением .asmx и строкой следующего вида.

<%@ WebService class="SampleService" language="C#" %>

Она означает, что при доступе к связанному с данным файлом URL по протоколам SOAP/HTTP будет активирована веб служба на основе класса SampleService. Класс веб службы может быть либо описан в этом же файле, либо помещен в сборке в подкаталоге bin. Класс веб службы не может быть классом общего вида. Класс веб службы обычно имеет необязательный атрибут System.Web.Services.WebServiceAttribute, имеющих два основных свойства – Description с текстовым описанием службы и Namespace с указанием уникального пространства имен, используемого при генерации описания интерфейса веб службы на языке WSDL. В его роли должен выступать уникальный URI, обычно связанный с адресом домена веб службы. Пространство имен не обязательно должно являться некоторым URL. Класс веб службы может быть унаследован от класса System.Web.Services.WebService. Необходимым на практике атрибутом класса веб службы при использовании WSE 3.0 является также атрибут Microsoft.Web.Services3.PolictyAttribute, который задает имя политики, в дальнейшем связываемое через файлы конфигурации с расширениями или списком допущенных пользователей.

Для того, чтобы включить метод класса веб службы в его общедоступный интерфейс, к методу применяется атрибут System.Web.Services.WebMethodAttribute. Он имеет два важных поля – Description и TransactionOption. Последний позволяет стать веб методу корневым объектом новой транзакции. Происходит это аналогично использованию сервисов без компонент COM+. Следует отметить, что веб метод может быть либо только корнем транзакции, либо не участвовать в ней. Поддержка распределенных между несколькими веб службами транзакций в .NET Framework 2.0 / WSE 3.0 отсутствует. Следующий веб метод является корнем новой транзакции.

<%@ WebService class="BankAccount" language="C#" %> using System; using System.Web.Services; using System.EnterpriseServices; using Microsoft.Web.Services3; [WebService(Namecpace="http://super.bank/webservices")] [Policy("BankAccountService")] public class BankAccount : WebService { [WebMethod(TransactionOption=TransactionOption.RequiresNew] public void Transfer(double Amount) { // ... // необязательное завершение транзакции в явном виде ContextUtil.SetComplete(); } }

Еще двумя важными атрибутами веб методов являются SoapRpcMethodAttribute и SoapDocumentMethodAttribute. Они определяют описание интерфейса метода на языке WSDL. В частности, при атрибуте SoapDocumentMethodAttribute будет использоваться кодирование параметров SOAP Document, что в настоящее время является стандартом, а при использовании SoapRpcMethodAttribute будет применяться кодирование SOAP RPC, в настоящее время признанное устаревшим. Следует отметить, что для преобразования содержимого тела пакета SOAP в параметры метода в обоих случаях используется класс XmlSerializer, поэтому на типы аргументов и результатов веб метода действуют все описанные в главе о сериализации ограничения, связанные с классом XmlSerializer. Атрибуты SoapDocumentMethod и SoapRpcMethod также содержат свойство OneWay, позволяющее клиенту производить односторонний вызов метода службы.



Реализация нестандартного расширения WSE


В качестве примера использования расширяемой архитектуры веб служб на основе ASP.NET представлено расширение WSE, которое ограничивает доступ к веб службе некоторым интервалом времени (например только с 10 часов вечера до 8 утра). Данному расширению достаточно создать единственный фильтр SOAP – входящий фильтр сервера. Далее приведен исходный текст сборки с расширением, унаследованным от абстрактного класса Microsoft.Web.Services3.Design.PolicyAssertion.

// TimeAssertion.cs using System; using System.IO; using System.Reflection; using System.Xml; using System.Xml.Serialization; using System.Collections.Generic; using Microsoft.Web.Services3; using Microsoft.Web.Services3.Design; using Microsoft.Web.Services3.Security; [assembly:AssemblyVersionAttribute("1.0.0.0")] namespace Seva.WS.Assertions { public class TimeAssertion: PolicyAssertion { private TimeIntervals intervals; public TimeAssertion(): base() { intervals = null; }

В методе ReadXml расширение должно считать свои параметры из файла политики. Единственным параметром расширения будет атрибут с именем файла с разрешенным интервалом времени.

public override void ReadXml(XmlReader reader, IDictionary<string, Type> extensions) { string fileName = reader.GetAttribute("file"); if (fileName == null) throw new Exception("Attribute 'file' not found in policy"); // Обязательная операция - переход к следующему тегу в файле политики, // иначе данный элемент будет читаться бесконечно reader.Read(); // Чтение файла с интервалами времени доступности службы using (StreamReader timeReader = new StreamReader(fileName)) { XmlSerializer serializer = new XmlSerializer(typeof(TimeIntervals)); intervals = (TimeIntervals) serializer.Deserialize( new XmlTextReader(timeReader)); } }

Методы Create....Filter отвечают за создание экземпляров фильтров SOAP, которые будут встроены в трубопровод фильтров. В данном примере только метод CreateServiceInputFilter возвращает созданный фильтр, но все остальные методы также обязаны присутствовать.


[XmlRoot("TimeIntervals")] public class TimeIntervals: List<TimeInterval> { public TimeIntervals() { } public bool Check(DateTime Time) { foreach ( TimeInterval interval in this) if (interval.Check(Time)) return true; return false; } } } Файлы сборки с расширениями WSE удобно поместить в глобальный кеш сборок GAC. Нижеследующий make файл создает и регистрирует сборку в GAC при команде nmake &amp;&amp; nmake install.

# Файл: makefile all: Seva.WS.TimeAssertion.dll Seva.WS.TimeAssertion.dll: TimeAssertion.cs TimeAssertion.key csc /t:library /out:Seva.WS.TimeAssertion.dll /keyfile:TimeAssertion.key /r:Microsoft.Web.Services3.dll TimeAssertion.cs TimeAssertion.key: sn -k TimeAssertion.key install: gacutil -nologo -i Seva.WS.TimeAssertion.dll` Для использования данной политики нужно выполнить следующие действия.

1. Создать файл с допустимым местным временем использования веб службы, например такой.

<?xml version="1.0" encoding="utf-8"?> <TimeIntervals> <TimeInterval Inverse="true" From="09:00:00" To="20:00:00" /> <TimeInterval Inverse="false" From="13:00:00" To="13:30:00" /> </TimeIntervals> 2. Создать файл wse3policyCache.config на сервере со следующим содержанием (для простоты предполагается отсутствие иных политик, также следует указать реальное значение публичного ключа сборки с созданным расширением WSE).

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy"> <extensions> <extension name="timeIntervals" type="Seva.WS.Assertions.TimeAssertion, Seva.WS.TimeAssertion, Version=1.0.0.0, Culture=neutral, PublicKeyToken=..." /> </extensions> <policy name="MyServicePolicy"> <timeIntervals file="C:\Inetpub\wwwroot\timeschema.config" /> </policy> </policy> </policies> Вместо MyServicePolicy должно быть указано название политики, указанной в атрибуте Policy класса веб службы.


Создание посредника веб службы


При использовании WSE 3.0 для создания посредника следует использовать утилиту WseWsdl3.exe. Она запускается следующим образом.

WseWsdl3.exe http://localhost/service.asmx?wsdl /type:webClient /out:proxy.cs

Утилита использует указанный URL WSDL описания службы и создаст на его основе файл proxy.cs с исходным текстом класса посредника, наследованного от Microsoft.Web.Services3.WebServicesClientProtocol. Описание веб сервиса на языке WSDL создается автоматически службой ASP.NET или иной системой веб служб. Поскольку файл c кодом посредника может быть создан заново в дальнейшем, в него не рекомендуется вносить какие либо изменения. Например, для связывания посредника с политикой WSE следует воспользоваться частичным описанием класса следующим образом.

[Policy("BankClientPolicy")] public partial class BankService : Microsoft.Web.Services3.WebServicesClientProtocol { }

Следует отметить, что, как и при применении утилиты xsd.exe для генерации кода по XML схеме, созданный посредник реализует не совсем тот же интерфейс, что исходный класс. Связано это с преобразованием типов данных CLR к типам данным XSD схем при создании описания интерфейса, и обратным преобразованием при создании посредника (рис. 7.5). В частности, как типизированные, так и нетипизированные списки класса службы в ее посреднике превращаются в массивы.


Рис. 7.5.  Создание посредника веб службы

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

Transfer – для обычного вызова веб метода;BeginTransfer и EndTransfer – для асинхронного вызова этого же метода через BeginInvoke/EndInvoke;TransferAsync (два метода) – для асинхронного вызова метода через InvokeAsync.

Разработчик может и самостоятельно использовать BeginInvoke/EndInvoke/InvokeAsync с аналогичным результатом, речь идет просто об экономии времени разработчика. В посреднике так же создается метод CancelAsync (один на каждый веб класс) для отмены вызовов удаленных методов через InvokeAsync.

Утилита WseWsdl3.exe поддерживает только работу с методами, имеющими единственный параметр или же не имеющими ни одного. Аналогичная по назначению стандартная утилита wsdl.exe из .NET SDK, не использующая WSE, не имеет такого ограничения. Данное ограничение следует считать оправданным и даже желательным для вызываемых удаленно методов, как объяснялось во второй главе данного курса.



Создание веб-службы в среде NET Framework


Будучи основаными на обмене сообщениями, веб службы поддерживают только одну модель удаленного вызова – активацию объекта сервером на время единственного удаленного вызова. Реализация веб сервисов в ASP.NET не имеет поддержки пула объектов. Доступ к каким либо свойствам или полям удаленного объекта невозможен, поскольку сообщения SOAP в ASP.NET связываются только с методами объекта.



Введение в веб службы


Веб службой или веб-сервисом (web service, WS) называется программная компонента, предоставляющая сервис удаленного вызова на основе группы стандартов WSI (Web Services Interoperability), основными из которых являются протокол обмена сообщениями SOAP, язык описания интерфейса WSDL, HTTP как основной транспортный протокол, а также XML и схемы XML. Для описания спецификаций формата сообщений в веб службах в настоящее время обычно используется схема XML и кодирование тела пакета SOAP Document.

Служба ASP.NET организует на основе данных стандартов сервис удаленного вызова методов объектов. ASP.NET создает и публикует WSDL описание веб службы на основе интерфейса класса .NET, обрабатывает приходящие от клиентов сообщения SOAP, вызывает методы объекта с извлеченными из сообщений SOAP параметрами, а затем передает клиенту сообщение с результатом выполнения метода (рис. 7.1). Таким образом, полное название промежуточной среды – веб службы ASP.NET.


Рис. 7.1.  Архитектура веб службы ASP.NET

Хотя с точки зрения прикладного разработчика использование веб службы представляется вариантом удаленного вызова через интернет, в основе веб служб лежит расширяемая технология обмена сообщениями SOAP, независимая от транспортного уровня. В частности, существуют стандарты, описывающие маршрутизацию сообщений SOAP, что придает веб службам признаки среды обмена сообщениями. Благодаря гибкой технологии возможно расширение промежуточной среды веб служб как по мере принятия новых стандартов, так и при необходимости в нестандартной обработке пакетов SOAP. Это возможно за счет фильтров SOAP, которые образуют трубопровод (pipeline), обрабатывающий пакеты SOAP. На платформе .NET Framework 2.0 за преобразование пришедшего по HTTP сообщения SOAP в вызов метода веб службы отвечает служба ASP.NET, а за организацию расширяемого набора фильтров SOAP – расширение, называемое WSE (Web Service Enhancements). В качестве приложения носителя для ASP.NET, получающего сообщение по транспортному протоколу, обычно используется служба Microsoft Internet Information Services, IIS (рис. 7.2).



Рис. 7.2.  Возможные схемы реализации веб службы
При использовании WSE 3. 0 возможно создание веб служб поверх протокола TCP без применения IIS. В приложении II так же приведен пример использования ASP.NET без использования IIS.
Клиентом веб службы ASP.NET может быть компонента, созданная на .NET Framework, или любая другая компонента, использующая требуемый сервисом набор стандартов веб служб. При использовании .NET Framework на основе WSDL описания сервиса специальные утилиты создают класс посредника веб службы, который используется клиентом сервиса. Таким же образом создаются на .NET Framework и клиенты веб сервисов, отличных от веб служб ASP.NET. Кроме того, в наиболее известной альтернативной реализации CLI – проекте Mono – в настоящий момент присутствует поддержка страниц и веб служб ASP.NET (версии 1.1 и частично 2.0) для серверов Apache и XSP.
Веб службы нашли широкое применение как средство построения распределенных гетерогенных приложений. В настоящее время разрабатываются все новые стандарты, связанные с веб службами, но не все еще имеют свою реализацию в виде, пригодном для промышленного применения. В частности, это касается стандартов WS Transaction и WS-BusinessActivity, которые дают возможность использовать распределенные транзакции, включающее в себя несколько веб служб. В настоящий момент эти стандарты только утверждаются, и будут реализованы, в частности, в .NET Framework 3.0. В настоящее же время веб службы ASP.NET могут являться только корневым объектом транзакции, но не могут участвовать в транзакциях, начатых их клиентом.
Широкое практическое применение веб служб привело и к тому, что некоторые связанные с ними стандарты уже устарели или не нашли столь широкого применения, как ожидалось. Прежде всего это касается стандарта UDDI (Universal Description, Discovery, and Integration), который является частью WS I и предназначается для поиска удаленных веб служб на основе создания соответствующих каталогов сервисов. Для построения сервера с каталогом веб служб можно использовать Microsoft Windows 2003 или систему на основе J2EE.Несколько крупных и известных компаний поддерживали публичные каталоги веб сервисов, однако эта инициатива была закрыта в конце 2005 года. Хотя идея поиска веб служб считалась одной из основополагающих, в настоящий момент нет публичных каталогов веб служб, а для развертывания даже в пределах крупных предприятий достаточно сложная спецификация UDDI представляется избыточной. Можно заключить, что на практике идея каталогов веб служб не нашла ожидаемого применения.

Выводы по использованию веб служб


Веб службы являются принятым индустриальным стандартом взаимодействия в распределенных системах. Их реализация на платформе .NET позволяет при использовании WSE полностью отделить политику доступа к службе от самого механизма службы. Проанализируем веб службы с точки зрения требований к распределенной системе.

Открытость. Веб сервисы построены на общепринятых стандартах и в силу этого являются открытой промежуточной средой. Они могут использоваться практически в любых сетях TCP/IP, в том числе через NAT и межсетевые экраны, поскольку обычно используют стандартный порт протокола HTTP. При необходимости веб службы могут быть использованы поверх различных транспортных протоколов.Масштабируемость, и устойчивость. Веб службы поддерживают только модель единственного вызова, поэтому балансировка нагрузок и обеспечение надежности сервиса может быть организовано путем создания кластера IIS или иными способами, оперирующими только с транспортным протоколом веб служб. Модель единственного вызова позволяет создавать масштабируемые приложения.Безопасность. Поддерживаемый WSE стандарт WS-Security 1.1 позволяет организовать разнообразные способы аутентификации пользователя, совмещенные с шифрованием сообщения и организации списка доступа к сервису. Поддержание логической целостности данных. Недостатком текущей реализации веб служб является отсутствие поддержки в настоящий момент распределенных между несколькими веб службами транзакций. В случае веб служб .NET данная проблема будет, вероятно, решена по мере перехода на .NET Framework 3.0 и WCF.Эффективность (в узком смысле). На передачу и разбор сообщений SOAP тратятся некоторые временные ресурсы, однако это является умеренной платой за открытый и расширяемый характер веб служб.

Все сказанное выше позволяет считать веб сервисы наиболее универсальной, открытой, масштабируемой и надежной технологией построения распределенных систем. Реализация механизма расширений WSE достаточно понятна и проста для использования разработчиком. После повсеместного принятия полного набора стандартов WS-* веб службы будут обладать широкими возможностями поддержки распределенных транзакций. Также определенным недостатком веб служб ASP.NET можно считать отсутствие в настоящий момент поддержки локального взаимодействия, не затрагивающего стек TCP/IP.