Как аутентифицировать веб-сервисы Primavera P6 с помощью токена имени пользователя в C#?
Мне нужно использовать токен имени пользователя для аутентификации веб-сервисов Primavera P6. Я создал консольное приложение в VS2010 и добавил ссылку на сервис:
http://localhost:8206/p6ws/services/ExportService?wsdl
Итак, на данный момент у меня есть прокси-класс, и я могу написать что-то вроде этого:
var exportService = new ExportPortTypeClient();
var project = new ExportProject { ProjectObjectId = 1000 };
exportService.ExportProject(project);
Пока я пытаюсь вызвать ExportProject(), я получаю исключение из-за сбоя аутентификации.
У кого-нибудь есть пример кода аутентификации с использованием токена имени пользователя?
2 ответа
Убедитесь, что вы создали клиент с пользовательской привязкой, например:
var securityElement = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
securityElement.AllowInsecureTransport = true; //in case you don't use SSL
securityElement.EnableUnsecuredResponse = true; //in case you don't use SSL
var encodingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
var transportElement = new HttpTransportBindingElement();
var binding = new CustomBinding(securityElement, encodingElement, transportElement);
EndpointAddress endpointAddress = new EndpointAddress("<your endpoint to service goes here>");
var exportService = new ExportPortTypeClient(binding, endpointAddress);
var project = new ExportProject { ProjectObjectId = 1000 };
exportService.ExportProject(project);
Наконец-то я нашел решение использовать токен имени пользователя с P6 WebServices. Это не так просто, как вы думаете. Вы должны включить заголовок WSE.
Решение для этого находится в блоге Рика Стрэла.
public class CustomTokenSerializer : WSSecurityTokenSerializer {
public CustomTokenSerializer(SecurityVersion sv)
: base(sv) { }
protected override void WriteTokenCore(XmlWriter writer, SecurityToken token) {
var userToken = token as UserNameSecurityToken;
var tokennamespace = "o";
var nonce = GetSHA1String(Guid.NewGuid().ToString());
writer.WriteRaw(
$@"<{tokennamespace}:UsernameToken u:Id=""{token.Id}"" xmlns:u=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"">
<{tokennamespace}:Username>{userToken.UserName}</{tokennamespace}:Username>
<{tokennamespace}:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\"">{userToken.Password}</{tokennamespace}:Password>
<{tokennamespace}:Nonce EncodingType=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\"">{nonce}</{tokennamespace}:Nonce>
</{tokennamespace}:UsernameToken>"
);
}
protected string GetSHA1String(string phrase) {
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
return Convert.ToBase64String(hashedDataBytes);
}
}
Я использую ChannelFactory для создания клиентов.
public class WebServiceClientFactory<T> : IWebServiceClientFactory<T> {
public WebServiceClientFactory() {
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.Expect100Continue = true;
ServicePointManager.DefaultConnectionLimit = 9999;
}
public T GetClient(Credentials cred) {
ChannelFactory<T> channelFactory = new ChannelFactory<T>(GetBinding(), new EndpointAddress(cred.Url));
channelFactory.Endpoint.Behaviors.Remove<System.ServiceModel.Description.ClientCredentials>();
channelFactory.Endpoint.Behaviors.Add(new CustomCredentials());
channelFactory.Endpoint.Behaviors.Add(new CustomP6DbInstanceBehavior(cred.DatabaseInstanceId));
channelFactory.Credentials.UserName.UserName = cred.Username;
channelFactory.Credentials.UserName.Password = cred.Password;
return channelFactory.CreateChannel();
}
private Binding GetBinding() {
var security = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
security.IncludeTimestamp = false;
var encoding = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
var transport = new HttpsTransportBindingElement {
MaxReceivedMessageSize = 20000000 // 20 megs
};
return new CustomBinding(security, encoding, transport);
}
}