Você viu, em artigos anteriores desta série, como usar WS-SecurityPolicy em descrições de serviço WSDL para configurar a manipulação de WS-Security. Uma desvantagem de WS-SecurityPolicy é a facilidade em cometer erros no desenvolvimento de uma política, por exemplo, usando uma asserção no lugar errado ou misturando versões de uma asserção no mesmo documento. Muitas soluções de serviços da Web ignoram silenciosamente erros desses tipos, o que significa que a política não funciona como deveria, e é preciso examiná-la para descobrir a causa.
Na primeira parte deste artigo, você aprenderá por que pode ser difícil trabalhar com WS-SecurityPolicy e verá as causas de erros comuns de WS-SecurityPolicy. A segunda parte apresenta um modelo de dados para WS-Policy e WS-SecurityPolicy que suporta a verificação da estrutura de documentos e mostra como a ligação de dados JiBX pode ser usada para fazer unmarshalling de documentos no modelo.
"Entendendo a WS-Policy" cobriu a maior parte dos aspectos da WS-Policy em detalhes, incluindo como asserções de política são anexadas a componentes WSDL 1.1. Em resumo, WS-Policy permite anexar políticas a definições de serviço WSDL em diversos pontos diferentes que correspondem a mensagens e agrupamentos de mensagens. Os quatro níveis de agrupamentos de mensagens usados pela WS-Policy são:
- Mensagem - A política se aplica a uma mensagem em particular (em qualquer lugar em que a mensagem seja usada, se a política for anexada pelo elemento
<wsdl:message>, ou quando usada por uma operação particular, se for anexada através das definições de entrada/saída/falha da operação no elemento<wsdl:portType>ou no elemento<wsdl:binding>). - Operação - A política se aplica a todas as trocas de mensagens para uma operação em particular (política anexada pelo elemento
<wsdl:operation>, em<wsdl:binding>ou<wsdl:portType>). - Terminal - A política se aplica a todas as trocas de mensagem para uma ligação de serviço em particular (política anexada por
<wsdl:port>ou<wsdl:binding>), ou para todas as ligações de serviço com base em um tipo de porta em particular (política anexada a<wsdl:portType>). - Serviço - A política se aplica a todos os terminais e todas as operações associadas a um serviço (política anexada no elemento
<wsdl:service>).
As políticas aplicadas em um nível dos agrupamentos de mensagens são herdadas por camadas inferiores, de modo que a política real (ou efetiva, em termos WS-Policy) aplicada a cada mensagem é o conjunto de todas as políticas aplicadas nas camadas de mensagem, operação, terminal e serviço. Então a política não é determinada somente pela mensagem, mas também pelo contexto no qual a mensagem é usada
Técnicas de anexação de WS-Policy
Em "Entendendo a WS-Policy," eu tratei da maneira mais comum de anexar asserções WS-Policy a componentes WSDL: usando o elemento <wsp:PolicyReference>. Esse tipo de anexação por referência é conveniente para a maioria dos propósitos, mas não é a única técnica. Também é possível usar um atributo especial — wsp:PolicyURIs — em qualquer elemento WSDL que corresponda a um dos níveis de agrupamento de mensagem. O valor do atributo é uma lista de URIs de asserção de política a serem aplicadas ao elemento. Também é possível anexar asserções de política a um elemento ESDL integrando a asserção diretamente.
A escolha entre as diferentes maneiras de anexar asserções de política é, em geral, arbitrária. O atributo wsp:PolicyURIs pode ser usado com alguns elementos WSDL que não permitem elementos de extensão de acordo com o esquema WSDL original — mas o esquema WSDL mais recente, WS-I, permite elementos de extensão em cada elemento WSDL, portanto, esse não é um problema para a maioria das soluções de serviços da Web. O uso do elemento <wsp:PolicyReference> ou do atributo wsp:PolicyURIs permite a reutilização de asserções de política, incluindo asserções de política externas (embora nem todas as soluções de serviços da Web suportem referências externas).
Há também uma maneira de anexar políticas externamente, usando um elemento <wsp:PolicyAttachment>. Em teoria, essa abordagem permite associar políticas com serviços de fora da WSDL. No entanto, a maioria das soluções de serviços da Web não é configurada para processar esse tipo de anexo. Em vez disso, as soluções esperam que as informações de política sejam referenciadas pela descrição de serviço WSDL ou incluídas diretamente na descrição.
<wsp:PolicyReference> permite definir um algoritmo de compilação e valor de compilação para a política referenciada, usando atributos opcionais. A compilação oferece um pouco de segurança para as referências de política externas, embora não proteja contra modificações na própria referência. <wsp:PolicyAttachment> vai mais além, permitindo que WS-Security seja usada diretamente para proteger todo o anexo.
Restrições de anexo WS-SecurityPolicy
WS-SecurityPolicy especifica os níveis de agrupamento de mensagem nos quais certos tipos de asserções de política devem ser anexados a uma descrição de serviço:
- Asserções de ligação de segurança:
<sp:TransportBinding>apenas no nível do terminal (pois o transporte é usado para acessar o terminal);<sp:SymmetricBinding>e<sp:AsymmetricBinding>em qualquer um dos terminais ou no nível da operação - Asserções de token de suporte (todas as formas de token de suporte, incluindo
<sp:SupportingTokens>e<sp:SignedSupportingTokens>, entre outros): nível de terminal, da operação ou da mensagem - Asserções de opção (
<sp:Wss10>,<sp:Wss11>,<sp:Trust13>): nível de terminal - Asserções de proteção (
<sp:SignedParts>,<sp:SignedElements>,<sp:EncryptedParts>,<sp:EncryptedElements>,<sp:ContentEncryptedElements>,<sp:RequiredElements>e<sp:RequiredParts>): nível de mensagem
Esses níveis são apenas sugestões, portanto, as pilhas de serviços da Web ser capazes de lidar com diferentes níveis de anexação — mas provavelmente é melhor seguir as sugestões sempre que possível.
As asserções de WS-SecurityPolicy que não aparecem acima estão aninhadas dentro de outras asserções, de modo que seu nível é controlado pela colocação da asserção que as contêm.
Trabalhando com WS-SecurityPolicy
WS-SecurityPolicy usa uma estrutura complexa e confusa que é difícil de desenvolver ou modificar diretamente. A maioria das soluções de serviços da Web comerciais fornece ferramentas GUI que podem ser usadas para gerar documentos de WS-SecurityPolicy a partir de menus de opção. (A solução Metro, de software livre, também fornece esse tipo de ferramenta, como parte de NetBeans). A saída de uma ferramenta desse tipo irá, provavelmente, funcionar bem com a solução de serviços da Web que a fornece, mas pode não estar de acordo com as boas práticas ou pode não estar completamente correta. Caso você não esteja usando uma ferramenta assim e queira ajustar uma configuração de segurança que não é suportada pela ferramenta, ou queira garantir uma política limpa, é preciso lidar diretamente com as complexidades de WS-SecurityPolicy.
Nunca gostei muito das ferramentas de configuração GUI da WS-SecurityPolicy, portanto, nesta série, trabalho diretamente com documentos de política. Durante o processo de testar diferentes tipos de configurações de segurança, cometi alguns erros que me deram um respeito permanente pela complexidade da WS-SecurityPolicy e pelas diversas maneiras como alguém pode estragá-la. Começarei falando dos motivos que me levam a considerar a WS-SecurityPolicy complexa e depois abordarei os problemas que, em minha experiência, interferem com maior frequência no desenvolvimento de configurações corretas e interoperáveis de WS-SecurityPolicy.
Estrutura de WS-SecurityPolicy
O esquema WS-SecurityPolicy 1.2 define cerca de 140 elementos, quase todos com definições globais (ou seja, cada um pode, em tese, ser usado de forma independente em vez de integrado dentro de outros elementos de um documento). A maior parte é definida como elementos de marcador vazios, com atributos de extensão de outros namespaces permitidos, mas sem conteúdo. Quanto ao restante, a maioria é definida como permitindo atributos de extensão e elementos de outros namespaces, mas não do namespace da WS-SecurityPolicy. O esquema 1.3 de WS-SecurityPolicy apenas estende a versão 1.2, incluindo mais alguns elementos.
Tanto o esquema 1.2 quanto a versão 1.3 são inúteis para qualquer fim prático. Isso não é culpa dos autores, mas sim das limitações do Esquema XML e da estrutura de WS-Policy.
Conforme discutido em "Entendendo a WS-Policy," a WS-Policy oferece uma estrutura básica para definir e combinar asserções de política, sendo que as próprias asserções são definidas por extensões como WS-SecurityPolicy, cada uma usando seu próprio namespace. A Listagem 1 traz um exemplo de um documento WS-SecurityPolicy, com os elementos WS-Policy mostrados em negrito:
Listagem 1. Exemplo de WS-SecurityPolicy
<wsp:Policy wsu:Id="SecureConv"
xmlns:wsu="http://.../oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsap:UsingAddressing xmlns:wsap="http://www.w3.org/2006/05/addressing/wsdl"/>
<sp:SymmetricBinding>
<wsp:Policy>
<sp:ProtectionToken>
<wsp:Policy>
<sp:SecureConversationToken sp:IncludeToken=
"http://.../ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:RequireDerivedKeys/>
<sp:BootstrapPolicy>
<wsp:Policy>
<sp:AsymmetricBinding>
<wsp:Policy>
<sp:InitiatorToken>
<wsp:Policy>
<sp:X509Token sp:IncludeToken=
"http://.../ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:RequireThumbprintReference/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:InitiatorToken>
<sp:RecipientToken>
<wsp:Policy>
<sp:X509Token sp:IncludeToken=
"http://.../ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator">
<wsp:Policy>
<sp:RequireThumbprintReference/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:RecipientToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:TripleDesRsa15/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Strict/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:OnlySignEntireHeadersAndBody/>
</wsp:Policy>
</sp:AsymmetricBinding>
<sp:SignedParts>
<sp:Body/>
<sp:Header Namespace="http://www.w3.org/2005/08/addressing"/>
</sp:SignedParts>
<sp:EncryptedParts>
<sp:Body/>
</sp:EncryptedParts>
<sp:Trust13>
<wsp:Policy>
<sp:MustSupportIssuedTokens/>
<sp:RequireClientEntropy/>
<sp:RequireServerEntropy/>
</wsp:Policy>
</sp:Trust13>
</wsp:Policy>
</sp:BootstrapPolicy>
</wsp:Policy>
</sp:SecureConversationToken>
</wsp:Policy>
</sp:ProtectionToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic128Rsa15/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Strict/>
</wsp:Policy>
</sp:Layout>
</wsp:Policy>
</sp:SymmetricBinding>
<sp:EncryptedParts>
<sp:Body/>
</sp:EncryptedParts>
</wsp:Policy>
|
O documento de política da Listagem 1 consiste, em sua maioria, de camadas alternadas de elementos WS-Policy (apenas o elemento <wsp:Policy>, neste caso) e elementos WS-SecurityPolicy (usando o prefixo sp). Infelizmente, o Esquema XML não é capaz de representar esse tipo de estrutura. Cada definição de esquema lida com um único namespace e, embora possam usar e se referir a outros namespaces, não podem definir uma subestrutura para esses elementos. Portanto, a definição de esquema de WS-Policy pode definir uma estrutura geral, e o esquema de WS-SecurityPolicy pode definir elementos para uso em conjunto com essas estruturas, mas não há maneira de definir as interações entre esses dois conjuntos de elementos.
Como o Esquema XML não é capaz de definir a estrutura de documentos, o texto do padrão de WS-SecurityPolicy usa a mesma representação de sintaxe que a recomendação de WS-Policy para mostrar a estrutura que os documentos devem ter. A Listagem 2 contém uma amostra do padrão, para o elemento <sp:SymmetricBinding> usado na Listagem 1:
Listagem 2. Amostra de sintaxe de WS-SecurityPolicy
<sp:SymmetricBinding xmlns:sp="..." ... >
<wsp:Policy xmlns:wsp="...">
(
<sp:EncryptionToken ... >
<wsp:Policy> ... </wsp:Policy>
</sp:EncryptionToken>
<sp:SignatureToken ... >
<wsp:Policy> ... </wsp:Policy>
</sp:SignatureToken>
) | (
<sp:ProtectionToken ... >
<wsp:Policy> ... </wsp:Policy>
</sp:ProtectionToken>
)
<sp:AlgorithmSuite ... > ... </sp:AlgorithmSuite>
<sp:Layout ... > ... </sp:Layout> ?
<sp:IncludeTimestamp ... /> ?
<sp:EncryptBeforeSigning ... /> ?
<sp:EncryptSignature ... /> ?
<sp:ProtectTokens ... /> ?
<sp:OnlySignEntireHeadersAndBody ... /> ?
...
</wsp:Policy>
...
</sp:SymmetricBinding>
|
A sintaxe usada na Listagem 2 deve ser bem fácil de ser entendida por desenvolvedores: usando parênteses para delimitar grupos, caracteres | para indicar escolhas entre alternativas e ? para indicar componentes opcionais. (A sintaxe completa também usa * para zero ou mais ocorrências e + para uma ou mais). Mas não é um padrão para gramáticas XML, por isso, não há uma maneira de usá-la diretamente para validar documentos de instância.
Dada a complexidade de WS-SecurityPolicy e a dificuldade de verificar documentos em relação ao padrão, não é surpresa que um dos problemas mais comuns ao trabalhar com documentos seja incluir asserções no lugar errado. Por exemplo, se o elemento <EncryptedParts> (próximo do final da Listagem 1) fosse movido para a Política do elemento <SymmetricBinding> precedente, o resultado seria uma estrutura que não corresponde ao padrão de WS-SecurityPolicy. A interpretação dessa política de estrutura incorreta seria indefinida e ficaria ao critério de cada solução de serviços da Web — talvez funcione como esperado em uma, mas pode não funcionar em outras.
Soluções de serviços da Web podem relatar ou não erros de estrutura ao usuário. Quando eu testei o exemplo acima, com um elemento EncryptedParts colocado incorretamente, nas versões mais recentes de três soluções de software livre (Apache Axis2 1.5.4, Metro 2.1 e Apache CXF 2.3.3), avaliadas em artigos anteriores desta série, apenas o Metro comunicou o erro e recusou-se a operar. Axis2 e CXF aceitaram a política sem reclamações, mas operaram sem criptografar o corpo das mensagens. Esse tipo de falha silenciosa torna difícil o diagnóstico de problemas causados por erros de estrutura de política.
Namespaces são uma questão especialmente problemática com WS-Policy e WS-SecurityPolicy. A normatização de WS-Policy e WS-SecurityPolicy levou anos, e as versões anteriores das tecnologias passaram a ser usadas antes que as normas fossem lançadas. As normas oficiais geralmente seguem a estrutura XML das versões anteriores, mas usam namespaces diferentes para que fique claro qual conjunto de regras se aplica a um documento em particular.
A maioria das ferramentas suporta as recomendações oficiais e as versões anteriores de WS-Policy e WS-SecurityPolicy, o que permite alternar entre as versões. (Axis2 é uma exceção, pois suporta apenas a versão proposta da WS-Policy no atual release 1.5.4). Apesar dessa flexibilidade, o uso de diferentes namespaces em um único documento pode gerar problemas. Não há motivo para misturar namespaces WS-Policy ou WS-SecurityPolicy intencionalmente, mas isso pode acontecer por acidente ao combinar partes de outras políticas para desenvolver uma nova política. Os conflitos resultantes podem incluir os namespaces usados para elementos e, no caso de valores de inclusão de token, os próprios valores de texto. Por exemplo, tanto WS-SecurityPolicy 1.2 como 1.3 usam o valor http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Always para indicar que um token deve sempre ser incluído em mensagens. A versão 1.1 mais antiga, pelo contrário, usa o valor http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Always para o mesmo propósito.
Como o significado de elementos XML permanece em grande parte inalterado ao longo das versões dessas normas, ferramentas individuais podem optar por ignorar as diferenças de namespace ao trabalhar com documentos de política. Ferramentas que adotam essa abordagem provavelmente aceitam sem erros políticas que misturam namespaces. Mas, em geral, é melhor manter o namespace da recomendação oficial e, especialmente, evitar misturar componentes de namespaces anteriores com componentes do namespace da recomendação.
Modelando WS-Policy e WS-SecurityPolicy
A modelagem de WS-Policy e WS-SecurityPolicy na linguagem Java apresenta um conjunto totalmente diferente de desafios em relação aos da modelagem de WSDL 1.1, como foi discutido em "Entendendo e Modelando o WSDL 1.1." Embora o problema principal na modelagem de WSDL seja permitir as muitas variações de ordenação de elemento em uso comum, WS-Policy e WS-SecurityPolicy ignoram completamente a ordenação de elementos, fornecendo, ao contrário, uma estrutura vaga com muitas regras impostas. Isso significa que o conteúdo esperado de um elemento <wsp:Policy> (ou qualquer das outras variações de operador WS-Policy) depende completamente do contexto do elemento. A Listagem 1 demonstra isso, com diferentes modelos de conteúdo para quase todos os 15 elementos <wsp:Policy> no documento.
As asserções de WS-SecurityPolicy se dividem, em sua maioria, em dois tipos: asserções de marcador (elementos vazios), que, apenas por sua presença, indicam alguma qualidade ou recurso; ou asserções estruturadas, com uma política aninhada que contém outras asserções. É nas asserções estruturadas que há complexidade, obviamente.
A manipulação para as asserções estruturadas é desenvolvida sobre a classe base NestedPolicy do modelo e a interface VerificationHandler associada, como mostra a Listagem 3:
Listagem 3.
NestedPolicy e VerificationHandler
public abstract class NestedPolicy extends AssertionBase {
/** Nested policy definition. */
private Policy m_policy;
/** Arbitrary extension elements. */
private List<Element> m_extensions = new ArrayList<Element>();
...
/**
* Create a handler instance for a set of assertions.
*
* @return instance
*/
public abstract VerificationHandler createHandler();
/**
* Verify policy goodness. This uses a handler supplied by the {@link
* #createHandler()} method to verify all the assertions within the nested policy in
* the context of this assertion.
*
* @param vctx
*/
public void verify(ValidationContext vctx) {
for (Set<AssertionBase> asserts : m_policy.getAlternatives()) {
VerificationHandler handler = createHandler();
for (AssertionBase asser : asserts) {
if (asser instanceof ExtensibleMarker) {
handler.addMarker((ExtensibleMarker)asser, vctx);
} else {
handler.addGeneral(asser, vctx);
}
}
handler.complete(vctx);
}
}
}
public interface VerificationHandler
{
/**
* Add marker assertion.
*
* @param marker
* @param vctx
*/
public abstract void addMarker(ExtensibleMarker marker, ValidationContext vctx);
/**
* Add general assertion (anything that's not a marker).
*
* @param asser
* @param vctx
*/
public abstract void addGeneral(AssertionBase asser, ValidationContext vctx);
/**
* Check that the assertions included in this collection fulfill all requirements for
* the policy. This method is only used when verifying a complete policy (one
* particular combination of alternatives, when using alternatives).
*
* @param vctx
* @return <code>true</code> if no errors, <code>false</code>
* if error
*/
boolean complete(ValidationContext vctx);
}
|
Todas as asserções estruturadas estendem a classe NestedPolicy, implementando o método createHandler() para retornar uma instância da interface VerificationHandler ajustada à estrutura de asserção específica. A VerificationHandler oferece métodos para acumular e verificar as asserções contidas na política aninhada, então, é nesta fase que os detalhes da manipulação da asserção estruturada são implementados. Quando alternativas de política estão presentes, o método NestedPolicy.verify() cria uma instância VerificationHandler separada para cada alternativa.
A Listagem 4 e a Listagem 5 mostram juntas um exemplo concreto de como isso funciona, na forma das classes usadas para representar e verificar a asserção SymmetricBinding. A classe SymmetricBinding na Listagem 4 é bem simples: apenas define uma classe interna com base na classe BindingAssertionHandler mostrada na Listagem 5 para manipulação da verificação:
Listagem 4.
SymmetricBinding
public class SymmetricBinding extends NestedPolicy {
public VerificationHandler createHandler() {
return new AssertionHandler();
}
private static class AssertionHandler extends BindingAssertionHandler
{
/** Containing element name. */
private static final String ELEMENT_NAME = "SymmetricBinding";
/** Names of allowed token role elements. */
private static final Set<String> TOKEN_ROLES =
VerificationHandlerBase.buildNameSet("EncryptionToken|...|ProtectionToken");
protected AssertionHandler() {
super(ELEMENT_NAME, TOKEN_ROLES,
BindingAssertionHandler.ENCRYPTION_BINDING_MARKERS);
}
public boolean complete(ValidationContext vctx) {
boolean good = true;
Map<String, TokenProperty> tokens = getRoleTokens();
if (tokens.containsKey("ProtectionToken")) {
if (tokens.containsKey("EncryptionToken")) {
vctx.reportError("sp:ProtectionToken conflicts ...", this);
good = false;
}
if (tokens.containsKey("SignatureToken")) {
vctx.reportError("sp:ProtectionToken conflicts ...", this);
good = false;
}
} else if (!tokens.containsKey("EncryptionToken") &&
!tokens.containsKey("SignatureToken")) {
vctx.reportWarning("No tokens defined for binding", this);
}
return super.complete(vctx) && good;
}
}
}
|
A classe interna SymmetricBinding.AssertionHandler define o conjunto de funções de token definidas para<sp:SymmetricBinding> e também implementa o método VerificationHandler.complete() para verificar se ao menos um tipo de token está presente e se há tokens em conflito. (<sp:SymmetricBinding> permite <sp:ProtectionToken>, que é usado para assinar e criptografar mensagens, ou <sp:EncryptionToken> e/ou <sp:SignatureToken> separados)
BindingAssertionHandler, mostrado na Listagem 5, é uma base em comum para manipular a verificação de todos os três tipos de asserções de ligação (transporte, assimétrica e simétrica). Cada uma define uma ou mais asserções de marcadores, uma ou mais funções de token, um <sp:AlgorithmSuite> exigido e um <sp:Layout> opcional (os dois últimos são asserções com asserções filhas de marcador, mas sem política aninhada).
Listagem 5.
BindingAssertionHandler
public class BindingAssertionHandler extends VerificationHandlerBase {
/** Names of marker elements allowed in <TransportBinding>. */
public static final Set<String> TRANSPORT_BINDING_MARKERS =
VerificationHandlerUtils.buildNameSet("IncludeTimestamp");
/** Names of marker elements allowed in ... or <SymmetricBinding>. */
public static final Set<String> ENCRYPTION_BINDING_MARKERS =
VerificationHandlerUtils.
buildNameSet("IncludeTimestamp|...|OnlySignEntireHeadersAndBody");
/** Actual element name. */
private final String m_elementName;
/** Roles allowed for tokens. */
private final Set<String> m_tokenRoles;
/** Token properties for binding. */
private final Map<String,TokenProperty> m_roleTokens;
/** Marker assertions allowed in policy. */
private final Set<String> m_knownMarkers;
/** Marker token assertions. */
private final Map<String,ExtensibleMarker> m_nameMarkers;
...
protected BindingAssertionHandler(String name, Set<String> roles,
Set<String> markers) {
m_elementName = name;
m_tokenRoles = roles;
m_roleTokens = new HashMap<String,TokenProperty>();
m_knownMarkers = markers;
m_nameMarkers = new HashMap<String,ExtensibleMarker>();
}
...
public void addMarker(ExtensibleMarker marker, ValidationContext vctx) {
String name = marker.getName();
if (m_knownMarkers.contains(name)) {
// generate warning for duplicate assertion
VerificationHandlerUtils.checkRepeat(marker, m_nameMarkers, vctx);
} else {
vctx.reportError("Assertion not allowed as child of sp:" + m_elementName,
marker);
}
}
public void addGeneral(AssertionBase asser, ValidationContext vctx) {
if (asser instanceof TokenProperty) {
TokenProperty token = (TokenProperty)asser;
String name = token.getName();
if (m_tokenRoles.contains(name)) {
TokenProperty prior = m_roleTokens.get(name);
if (prior == null) {
m_roleTokens.put(name, token);
} else {
vctx.reportError("Duplicate token ", asser);
}
} else {
vctx.reportError("Token not allowed as child of sp:" + m_elementName,
asser);
}
} else if (asser instanceof AlgorithmSuite) {
...
} else {
vctx.reportError("Assertion not allowed as child of sp:" + m_elementName,
asser);
}
}
public boolean complete(ValidationContext vctx) {
if (m_algorithmSuite == null) {
vctx.reportError("Missing required sp:AlgorithmSuite property", this);
return false;
} else {
return true;
}
}
}
|
Tanto a Listagem 4 como a Listagem 5 usam VerificationHandlerUtils.buildNameSet() para desenvolver um conjunto de nomes para um valor de sequência. Esse método quebra a sequência de entrada em caracteres | (barra vertical) para encontrar os nomes individuais a serem incluídos no conjunto. O resultado é um código muito mais conciso que o que seria obtido se os nomes fossem passados individualmente. Os conjuntos de nomes são, então, usados para verificar asserções permitidas na política aninhada.
Fazendo unmarshalling do modelo
O uso de diversos namespaces com basicamente os mesmos dados cria alguns problemas graves para a ligação de dados XML. A maioria das ferramentas de ligação de dados poderia lidar apenas com esses diversos namespaces, criando conjuntos separados de classes para cada namespace. A ligação de dados JiBX pode fazer melhor, usando várias ligações para as mesmas classes. Cada ligação pode usar os mesmos nomes de elemento para cada classe, mas em namespaces diferentes.
A estrutura vaga de WS-Policy e WS-SecurityPolicy também causa problemas para a ligação de dados, mas aqui JiBX também pode lidar de forma limpa com os dados, com um pouco de esforço adicional. JiBX suporta código de extensão de usuário para unmarshalling (e marshalling) de estruturas de dados que, de outra maneira, não poderiam ser ligadas a XML. Eu usei vários unmarshallers customizados para lidar com os dados de WS-Policy e WS-SecurityPolicy. O mais interessante deve ser OperatorUnmarshaller, usado para fazer unmarshalling de qualquer dos operadores WS-Policy e de suas asserções filhas. A Listagem 6 mostra o código para esse unmarshaller:
Listagem 6.
OperatorUnmarshaller
public class OperatorUnmarshaller implements IUnmarshaller, IAliasable {
...
public boolean isPresent(IUnmarshallingContext ictx) throws JiBXException {
UnmarshallingContext ctx = (UnmarshallingContext)ictx;
ctx.toTag();
if (PolicyNamespace.LOOKUP.getNamespace(ctx.getElementNamespace()) != null) {
String name = ctx.getElementName();
return "Policy".equals(name) || "ExactlyOne".equals(name) ||
"All".equals(name);
}
return false;
}
public Object unmarshal(Object obj, IUnmarshallingContext ictx) ... {
if (isPresent(ictx)) {
return unmarshalOperator((UnmarshallingContext)ictx);
} else {
return null;
}
}
private OperatorBase unmarshalOperator(UnmarshallingContext ctx) ... {
// create the instance to be returned
NamespaceInfo ns = PolicyNamespace.LOOKUP.
getNamespace(ctx.getElementNamespace());
if (ns == null) {
throw new IllegalStateException("Internal error - ...");
}
Policy policy = Policy.getNestedPolicy(ctx);
PolicyNamespace prior = policy == null ?
null : (PolicyNamespace)policy.checkNamespace(ns);
Policy policy = Policy.getNestedPolicy(ctx);
String name = ctx.getElementName();
OperatorBase operator;
if ("Policy".equals(name)) {
policy = new Policy(policy, ns);
operator = policy;
} else if ("ExactlyOne".equals(name)) {
operator = new OperatorBase.ExactlyOne(ns);
} else {
operator = new OperatorBase.All(ns);
}
// check for namespace conflict
if (prior != null && ns != prior) {
((ValidationContext)ctx.getUserContext()).reportError("Policy namespace " +
ns.getUri() + " conflicts with containing policy namespace " +
prior.getUri(), operator);
}
// track object and handle all attributes
ctx.pushTrackedObject(operator);
operator.readAttributes(ctx);
ctx.nextToken();
// process all child elements
while (ctx.toTag() == IXMLReader.START_TAG) {
if (isPresent(ctx)) {
// unmarshal child policy operator
operator.getChildOperators().add(unmarshalOperator(ctx));
} else {
String uri = ctx.getElementNamespace();
name = ctx.getElementName();
IUnmarshaller unmarshaller = ctx.getUnmarshaller(uri, name);
if (unmarshaller == null) {
// treat unmapped element from known namespace as marker assertion
ns = policy.getNamespace(uri);
if (ns != null) {
operator.getChildAssertions().add
(ExtensibleMarkerUnmarshaller.unmarshal(ctx, ns));
} else {
// just use DOM for element from unknown namespace
...
}
} else {
// unmarshal known child element as policy assertion
Object object = unmarshaller.unmarshal(null, ctx);
if (object instanceof AssertionBase) {
operator.getChildAssertions().add((AssertionBase)object);
} else {
throw new JiBXException("Internal error - child element ...");
}
}
}
}
ctx.nextToken();
ctx.popObject();
return operator;
}
}
|
A interface IUnmarshaller define apenas dois métodos: isPresent() para verificar se a tag de início do elemento atual é manipulada pelo unmarshaller e unmarshal() para fazer unmarshalling dos dados de um elemento. No código da Listagem 6, o método isPresent() verifica se o namespace do elemento atual corresponde a uma das versões da WS-Policy e se o nome do elemento corresponde a um dos três nomes de operador de política (Policy, ExactlyOne ou All).
O método unmarshal() também é simples, mas apenas porque delega todo o trabalho ao método unmarshalOperator(). unmarshalOperator() considera que o usuário está posicionado em um dos elementos de operador de política e começa criando uma instância da classe de operador correspondente, usando o namespace apropriado da WS-Policy (verificando se o namespace usado nesse operador corresponde àquele no elemento <wsp:Policy> que o contém, se for o caso). Em seguida, ele executa um loop para fazer unmarshalling de todos os elementos filhos. Há quatro maneiras de lidar com elementos filhos:
- Se o filho for outro operador de política, chamar
unmarshalOperator()recursivamente. - Se houver um unmarshaller para esse elemento (ou seja, a definição de ligação contém uma definição de mapeamento para o elemento), chamar o unmarshaller.
- Se o namespace do elemento for reconhecido como um namespace de extensão de política, fazer unmarshalling como uma asserção de marcador vazia.
- Do contrário, tratar como um elemento de extensão não classificado e usar uma representação DOM.
A terceira opção significa que elementos de marcador não precisam ser nomeados nas definições de ligação JiBX, que ajudam a manter as ligações relativamente simples (e também não requerem classes individuais, o que mantém as estruturas de dados relativamente simples). No entanto, essas ligações precisam estabelecer definições mapping JiBX para todas as asserções que não sejam de marcador, e ligações separadas devem ser usadas para cada namespace. A Listagem 7 mostra a ligação de nível máximo, que contém mapeamentos abstratos comuns (que não são associados a qualquer nome de elemento e, portanto, podem ser reutilizados em vários namespaces) para WS-Policy e WS-SecurityPolicy:
Listagem 7. Definição de ligação de unmarshalling de nível máximo
<binding package="com.sosnoski.ws" trim-whitespace="true"
value-style="attribute" force-classes="true" direction="input" track-source="true">
<include path="in-policy-200409.xml"/>
<include path="in-policy-200702.xml"/>
<include path="in-secpolicy-200507.xml"/>
<include path="in-secpolicy-200702.xml"/>
<!-- Base marker element mapping -->
<mapping class="com.sosnoski.ws.policy.ExtensibleMarker" unmarshaller=
"com.sosnoski.ws.secpolicy.SecurityPolicyNamespace$SecurityPolicyMarkerUnmarshaller"/>
<!-- Basic nested policy mapping -->
<mapping abstract="true" class="com.sosnoski.ws.secpolicy.NestedPolicy"
pre-set="preset" ordered="false" allow-repeats="true">
<structure set-method="setPolicy" usage="optional"
unmarshaller="com.sosnoski.ws.policy.OperatorUnmarshaller"/>
<structure type="org.w3c.dom.Element" unmarshaller="org.jibx.extras.DomElementMapper"
set-method="addExtension" usage="optional"/>
</mapping>
...
<!-- Token base mapping -->
<mapping abstract="true" class="com.sosnoski.ws.secpolicy.TokenBase"
ordered="false" allow-repeats="true">
<structure map-as="com.sosnoski.ws.secpolicy.NestedPolicy"/>
<structure name="Issuer" set-method="setIssuer" usage="optional"
unmarshaller="com.sosnoski.ws.policy.ExtensibleValueUnmarshaller"/>
<structure name="IssuerName" set-method="setIssuerName" usage="optional"
unmarshaller="com.sosnoski.ws.policy.ExtensibleValueUnmarshaller"/>
</mapping>
<!-- Token property base mapping -->
<mapping abstract="true" class="com.sosnoski.ws.secpolicy.TokenProperty"
pre-set="preset" ordered="false" allow-repeats="true">
<structure map-as="com.sosnoski.ws.secpolicy.NestedPolicy"/>
</mapping>
<!-- Base handling for protection specifications -->
<mapping abstract="true" class="com.sosnoski.ws.secpolicy.ProtectParts"
pre-set="preset" ordered="false" allow-repeats="true">
<structure name="Body" set-method="setBody" usage="optional"
unmarshaller="com.sosnoski.ws.secpolicy.SecurityMarkerUnmarshaller"/>
<structure name="Attachments" set-method="setAttachments" usage="optional"
unmarshaller="com.sosnoski.ws.secpolicy.SecurityMarkerUnmarshaller"/>
<structure name="Header" set-method="addHeader" usage="optional"
unmarshaller="com.sosnoski.ws.secpolicy.Header$HeaderUnmarshaller"/>
</mapping>
</binding>
|
A Listagem 8 mostra um par de ligações de namespace específicas de versão que são referenciadas pelos elementos <include> da ligação da Listagem 7, um para o namespace de WS-Policy e outro para o namespace de WS-SecurityPolicy. Eles associam as classes de modelo de dados de namespace independentes de versão com nomes de elementos em um namespace em particular e passam a manipulação para classes de unmarshaller específicas (no caso de elementos de operador da WS-Policy, a classe OperatorUnmarshaller da Listagem 6) ou delegando a um dos mapeamentos abstratos da ligação da Listagem 7.
Listagem 8. Definições de ligação de unmarshalling de WS-Policy e WS-SecurityPolicy
<binding value-style="attribute" force-classes="true" direction="input"
track-source="true">
<!-- Make the recommendation namespace the default -->
<namespace uri="http://schemas.xmlsoap.org/ws/2004/09/policy"
default="elements" prefix="wsp"/>
<!-- Define all supported policy elements -->
<mapping name="Policy" class="com.sosnoski.ws.policy.Policy"
unmarshaller="com.sosnoski.ws.policy.OperatorUnmarshaller"/>
<mapping name="ExactlyOne" class="com.sosnoski.ws.policy.OperatorBase$ExactlyOne"
unmarshaller="com.sosnoski.ws.policy.OperatorUnmarshaller"/>
<mapping name="All" class="com.sosnoski.ws.policy.OperatorBase$All"
unmarshaller="com.sosnoski.ws.policy.OperatorUnmarshaller"/>
<mapping class="com.sosnoski.ws.policy.PolicyReference" name="PolicyReference">
<structure map-as="PolicyReference"/></mapping>
</binding>
<binding value-style="attribute" force-classes="true" direction="input"
track-source="true">
<!-- Make the WS-SecurityPolicy 1.1 namespace the default -->
<namespace uri="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"
default="elements" prefix="sp1"/>
<!-- Token variations -->
<mapping name="SecureConversationToken"
class="com.sosnoski.ws.secpolicy.SecureConversationToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenBase"/></mapping>
<mapping name="X509Token" class="com.sosnoski.ws.secpolicy.X509Token">
<structure map-as="com.sosnoski.ws.secpolicy.TokenBase"/></mapping>
...
<!-- Token property variations -->
<mapping name="InitiatorToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$InitiatorToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
<mapping name="InitiatorSignatureToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$InitiatorSignatureToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
<mapping name="InitiatorEncryptionToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$InitiatorEncryptionToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
<mapping name="RecipientToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$RecipientToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
<mapping name="RecipientSignatureToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$RecipientSignatureToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
<mapping name="RecipientEncyrptionToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$RecipientEncyrptionToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
<mapping name="ProtectionToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$ProtectionToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
<mapping name="EncryptionToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$EncryptionToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
<mapping name="SignatureToken"
class="com.sosnoski.ws.secpolicy.TokenProperty$SignatureToken">
<structure map-as="com.sosnoski.ws.secpolicy.TokenProperty"/></mapping>
...
<!-- Definir outras asserções que contêm políticas aninhadas -->
<mapping name="AlgorithmSuite" class="com.sosnoski.ws.secpolicy.AlgorithmSuite">
<structure map-as="com.sosnoski.ws.secpolicy.NestedPolicy"/></mapping>
<mapping name="AsymmetricBinding" class="com.sosnoski.ws.secpolicy.AsymmetricBinding">
<structure map-as="com.sosnoski.ws.secpolicy.NestedPolicy"/></mapping>
<mapping name="BootstrapPolicy" class="com.sosnoski.ws.secpolicy.BootstrapPolicy">
<structure map-as="com.sosnoski.ws.secpolicy.NestedPolicy"/></mapping>
<mapping name="Layout" class="com.sosnoski.ws.secpolicy.Layout">
<structure map-as="com.sosnoski.ws.secpolicy.NestedPolicy"/></mapping>
<mapping name="SymmetricBinding" class="com.sosnoski.ws.secpolicy.SymmetricBinding">
<structure map-as="com.sosnoski.ws.secpolicy.NestedPolicy"/></mapping>
<mapping name="Trust13" class="com.sosnoski.ws.secpolicy.Trust13">
<structure map-as="com.sosnoski.ws.secpolicy.NestedPolicy"/></mapping>
...
<!-- Other elements with specific handling -->
<mapping name="SignedParts" class="com.sosnoski.ws.secpolicy.ProtectParts$SignedParts"
factory="com.sosnoski.ws.secpolicy.ProtectParts.newSignedParts">
<structure map-as="com.sosnoski.ws.secpolicy.ProtectParts"/></mapping>
<mapping name="EncryptedParts"
class="com.sosnoski.ws.secpolicy.ProtectParts$EncryptedParts"
factory="com.sosnoski.ws.secpolicy.ProtectParts.newEncryptedParts">
<structure map-as="com.sosnoski.ws.secpolicy.ProtectParts"/></mapping>
...
</binding>
|
Mesmo com JiBX, o unmarshalling de documentos WS-Policy e WS-SecurityPolicy envolve um grau significativo de complexidade. Mas a combinação de definições de ligação com código de extensão do usuário para unmarshalling torna a tarefa muito mais fácil do que seria com ferramentas de ligação de dados que adotam uma abordagem mais rígida para a manipulação de XML.
Neste artigo, você conheceu alguns dos problemas que tornam a WS-SecurityPolicy difícil de entender e alguns dos erros comuns cometidos ao trabalhar com documentos WS-SecurityPolicy. Você também viu a base para um modelo de dados Java para a combinação de WS-Policy e WS-SecurityPolicy que suporta verificação de documentos WS-SecurityPolicy e aprendeu como a ligação de dados JiBX pode ser usada para fazer unmarshalling dos documentos para desenvolver o modelo.
O próximo artigo irá concluir o desenvolvimento de uma ferramenta para verificar e reformatar documentos WSDL e WS-Policy/WS-SecurityPolicy. Nele, você saberá mais sobre como o modelo lida com alternativas de política e outros aspectos de verificação, e como a ligação de dados JiBX é usada para gerar documentos convertidos para a versão atual das normas em um formato compatível com as boas práticas.
Aprender
-
W3C Web Services Policy Working Group: Esse grupo define a especificação da WS-Policy, sendo WS-Policy 1.5 a versão atual. Seu trabalho se baseia na proposta WS-Policy 1.2, uma proposta desenvolvida por um grupo de empresas interessadas. Tanto a versão proposta 1.2 da WS-Policy como a recomendação oficial 1.5 são amplamente usadas (e ao menos uma solução de serviços da Web, Axis2, suporta atualmente apenas a versão proposta).
-
OASIS Web Services Secure Exchange (WS-SX) TC: Essa organização é responsável pela WS-SecurityPolicy e desenvolveu a versão atual, 1.3 (que estende a versão anterior da WS-SecurityPolicy, 1.2, mantendo o namespace da WS-SecurityPolicy 1.2 para os elementos em comum). Seu trabalho se baseia na proposta WS-SecurityPolicy 1.1, novamente uma proposta desenvolvida por um grupo de empresas interessadas. A maioria dos aplicativos está usando agora a recomendação oficial WS-SecurityPolicy 1.2/1.3, embora a versão proposta WS-SecurityPolicy 1.1 ainda seja amplamente usada.
-
Navegue pela livraria de tecnologia para obter livros sobre estes e outros tópicos técnicos.
-
Zona tecnologia Java do developerWorks: Encontre centenas de artigos sobre quase todos os aspectos da programação Java.
Obter produtos e tecnologias
-
Apache Neethi: Neethi é uma de várias ferramentas de software livre para trabalhar com WS-Policy. Neethi, como a maioria das outras ferramentas de política disponíveis, é baseada em uma representação DOM de XML. Ele não suporta diretamente WS-SecurityPolicy ou outras extensões da WS-Policy, mas foi projetado para ser estendido por aplicativos para fornecer esse suporte.
-
Ligação de dados JiBX: JiBX é uma ferramenta para conversão entre documentos XML e modelos de dados Java. Ela oferece um melhor desempenho e maior flexibilidade do que outras abordagens de ligação de dados, como JAXB, o que a torna especialmente útil para estruturas difíceis de documento que não se encaixam em definições de esquema simples.
Discutir
- Participe da comunidade do developerWorks.

Dennis Sosnoski é um consultor e instrutor especializado em XML e serviços da Web baseados em Java. Sua experiência em desenvolvimento de software profissional se estende por mais de 30 anos, sendo que nos últimos 10 focou tecnologias XML e Java do lado do servidor. Dennis é o desenvolvedor líder da estrutura de software livre JiBX XML Data Binding e a estrutura de serviços da Web associada JiBX/WS, assim como um committer na estrutura de serviços da Web Apache Axis2. Também foi um dos membros do Grupo de Especialistas para as especificações JAX-WS 2.0 e JAXB 2.0. O material para a série Serviços da Web Java é baseado nas aulas de treinamento de Dennis.