ВНИМАНИЕ: В этом примере требуется Python 2.7 или выше.
Встроенный в Python объект Counter
- это именно то, что вы ищете. Подсчет слов является даже первым примером в документации:
>>> # Tally occurrences of words in a list
>>> from collections import Counter
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
... cnt[word] += 1
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
Как указано в комментариях, Counter
использует итерацию, поэтому приведенный выше пример приведен только для иллюстрации и эквивалентен:
>>> mywords = ['red', 'blue', 'red', 'green', 'blue', 'blue']
>>> cnt = Counter(mywords)
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
Я нашел решение своей проблемы, однако оно оказалось намного неприятнее, чем я ожидал.
По сути, для проверки учетных данных транспорта и сообщений вам необходимо определить пользовательская привязка. (Я нашел информацию по этому поводу здесь ).
Я обнаружил, что самый простой способ сделать это - продолжить настройку в XML, но во время выполнения скопировать и немного изменить привязку netTcp из XML конфигурация. Вам нужно включить буквально один переключатель. Вот код на стороне службы и на стороне клиента:
Сторона службы
ServiceHost businessHost = new ServiceHost(typeof(DHTestBusinessService));
ServiceEndpoint endpoint = businessHost.Description.Endpoints[0];
BindingElementCollection bindingElements = endpoint.Binding.CreateBindingElements();
SslStreamSecurityBindingElement sslElement = bindingElements.Find<SslStreamSecurityBindingElement>();
sslElement.RequireClientCertificate = true; //Turn on client certificate validation
CustomBinding newBinding = new CustomBinding(bindingElements);
NetTcpBinding oldBinding = (NetTcpBinding)endpoint.Binding;
newBinding.Namespace = oldBinding.Namespace;
endpoint.Binding = newBinding;
Сторона клиента
DHTestBusinessServiceClient client = new DHTestBusinessServiceClient();
ServiceEndpoint endpoint = client.Endpoint;
BindingElementCollection bindingElements = endpoint.Binding.CreateBindingElements();
SslStreamSecurityBindingElement sslElement = bindingElements.Find<SslStreamSecurityBindingElement>();
sslElement.RequireClientCertificate = true; //Turn on client certificate validation
CustomBinding newBinding = new CustomBinding(bindingElements);
NetTcpBinding oldBinding = (NetTcpBinding)endpoint.Binding;
newBinding.Namespace = oldBinding.Namespace;
endpoint.Binding = newBinding;
Вы могли подумать, что это все, но ошиблись! :) Вот где он становится лишним. Я приписывал своим конкретным методам службы PrincipalPermission ограничение доступа на основе ролей пользователя службы, например:
[PrincipalPermission(SecurityAction.Demand, Role = "StandardUser")]
Это начало давать сбой, как только я применил вышеупомянутые изменения. Причина заключалась в том, что
OperationContext.Current.ServiceSecurityContext.PrimaryIdentity
в конечном итоге оказался неизвестным, не прошедшим проверку подлинности лицом II. Это произошло потому, что на самом деле существует два идентификатора, представляющих пользователя: один для сертификата X509, используемого для аутентификации через транспорт, и один для учетных данных имени пользователя и пароля, используемых для аутентификации на уровне сообщений. Когда я перепроектировал двоичные файлы WCF, чтобы понять, почему он не дает мне мой PrimaryIdentity, я обнаружил, что в нем есть явная строка кода, которая заставляет его возвращать этот пустой IIdentity, если он находит более одного IIdentity. Я предполагаю, что это потому, что у него нет возможности определить, какой из них является основным .
Это означает, что использование атрибута PrincipalPermission невозможно. Вместо, Я написал метод, имитирующий его функциональность, который может иметь дело с несколькими IIdentity:
private void AssertPermissions(IEnumerable<string> rolesDemanded)
{
IList<IIdentity> identities = OperationContext.Current.ServiceSecurityContext.AuthorizationContext.Properties["Identities"] as IList<IIdentity>;
if (identities == null)
throw new SecurityException("Unauthenticated access. No identities provided.");
foreach (IIdentity identity in identities)
{
if (identity.IsAuthenticated == false)
throw new SecurityException("Unauthenticated identity: " + identity.Name);
}
IIdentity usernameIdentity = identities.Where(id => id.GetType().Equals(typeof(GenericIdentity))).SingleOrDefault();
string[] userRoles = Roles.GetRolesForUser(usernameIdentity.Name);
foreach (string demandedRole in rolesDemanded)
{
if (userRoles.Contains(demandedRole) == false)
throw new SecurityException("Access denied: authorisation failure.");
}
}
Это некрасиво (особенно то, как я определяю учетные данные имени пользователя и пароля IIdentity), но он работает! Теперь, в верхней части моих служебных методов, мне нужно назвать это так:
AssertPermissions(new [] {"StandardUser"});
ознакомьтесь с разделом Codeplex - Интранет , который предоставляет контрольные списки для различных сценариев. Также следует упомянуть, что netTcpBindings не поддерживаются для использования в IIS5.0 и IIS6.0 - см. 3-й абзац здесь , только IIS7.0 +.