Não é possível autenticar com jax-ws no Glassfish

Eu tenho tentado criar um serviço java que usa autenticação básica sobre SSL. Configurar o SSL foi bastante simples, mas configurar a autenticação é um problema muito maior. Sempre que tento chamar um método marcado com uma anotação @RolesAllowed, recebo a seguinte exceção …

Exception in thread "AWT-EventQueue-0" javax.xml.ws.soap.SOAPFaultException: javax.ejb.EJBAccessException at com.sun.xml.internal.ws.fault.SOAP11Fault.getProtocolException(SOAP11Fault.java:178) at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:111) at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:108) at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78) at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:129) at $Proxy30.reverseString(Unknown Source) at securitytestclient.SecurityTestClient.reverseStringSOAP(SecurityTestClient.java:305) at securitytestclient.SecurityTestClient.buttonReverseActionPerformed(SecurityTestClient.java:217) at securitytestclient.SecurityTestClient.access$000(SecurityTestClient.java:20) at securitytestclient.SecurityTestClient$1.actionPerformed(SecurityTestClient.java:72) at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2018) at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2341) at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402) at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259) at javax.swing.plaf.basic.BasicButtonListener$Actions.actionPerformed(BasicButtonListener.java:303) at javax.swing.SwingUtilities.notifyAction(SwingUtilities.java:1661) at javax.swing.JComponent.processKeyBinding(JComponent.java:2879) at javax.swing.JComponent.processKeyBindings(JComponent.java:2926) at javax.swing.JComponent.processKeyEvent(JComponent.java:2842) at java.awt.Component.processEvent(Component.java:6282) at java.awt.Container.processEvent(Container.java:2229) at java.awt.Component.dispatchEventImpl(Component.java:4861) at java.awt.Container.dispatchEventImpl(Container.java:2287) at java.awt.Component.dispatchEvent(Component.java:4687) at java.awt.KeyboardFocusManager.redispatchEvent(KeyboardFocusManager.java:1890) at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(DefaultKeyboardFocusManager.java:752) at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(DefaultKeyboardFocusManager.java:1017) at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(DefaultKeyboardFocusManager.java:889) at java.awt.DefaultKeyboardFocusManager.dispatchEvent(DefaultKeyboardFocusManager.java:717) at java.awt.Component.dispatchEventImpl(Component.java:4731) at java.awt.Container.dispatchEventImpl(Container.java:2287) at java.awt.Window.dispatchEventImpl(Window.java:2713) at java.awt.Component.dispatchEvent(Component.java:4687) at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:707) at java.awt.EventQueue.access$000(EventQueue.java:101) at java.awt.EventQueue$3.run(EventQueue.java:666) at java.awt.EventQueue$3.run(EventQueue.java:664) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:87) at java.awt.EventQueue$4.run(EventQueue.java:680) at java.awt.EventQueue$4.run(EventQueue.java:678) at java.security.AccessController.doPrivileged(Native Method) at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) at java.awt.EventQueue.dispatchEvent(EventQueue.java:677) at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:211) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:128) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:117) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:113) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:105) at java.awt.EventDispatchThread.run(EventDispatchThread.java:90) 

Meu servidor está usando o JavaEE no Glassfish 3. Suas funções são definidas em um bean da seguinte forma …

 package com.intproimp.testing.beans; import javax.annotation.security.PermitAll; import javax.annotation.security.RolesAllowed; import javax.ejb.Local; import javax.ejb.Stateless; @Stateless(mappedName="ejb/StringOps/Bean") @Local(StringOps.class) public class StringOpsBean implements StringOps { @PermitAll @Override public String echoString(String str) { return str; } @RolesAllowed({"admin"}) @Override public String reverseString(String str) { char[] input = str.toCharArray(); char[] output = new char[input.length]; for (int i = 0; i < output.length; i++) { output[i] = input[input.length - i - 1]; } return new String(output); } } 

Eu adicionei a configuração de login ao meu arquivo web.xml …

  BASIC testing-frealm    admin    user  

O domínio de segurança é apenas um domínio de arquivo com um usuário (nome: “andrew”, passar: “12345”). Eu também adicionei os mapeamentos de grupos de funções ao meu glassfish-web.xml

  user user   admin admin  

No lado do cliente, tenho um aplicativo de swing simples para testar …

Aplicação de Teste Swing

A chamada que está sendo feita é para a function inversa em um canal SOAP. Os componentes do cliente SOAP são gerados com o NetBeans por meio da function New-> WebService Client. E a ação associada ao clique do botão reverso é …

 private void addAuthenticateionSOAP(StringOpsSOAP port) { ((BindingProvider) port).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, fieldUsername.getText()); ((BindingProvider) port).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, fieldPassword.getText()); } private String reverseStringSOAP(String str) { StringOpsSOAP_Service service = new StringOpsSOAP_Service(); StringOpsSOAP port = service.getStringOpsSOAPPort(); if (checkAuthentication.isSelected()) addAuthenticateionSOAP(port); return port.reverseString(str); } 

Tenho certeza de que há apenas uma pequena coisa que estou perdendo, mas tenho olhado para o problema há dias e ainda não o encontrei.

– Edit – Eu percebi que poderia haver alguma confusão desde que eu não postei o código do meu WebService. O EJB que contém as funções String é acessado através de um SOAP WebService Bean …

 package com.intproimp.test.web; import com.intproimp.test.beans.StringOps; import javax.ejb.EJB; import javax.jws.WebService; import javax.ejb.Stateless; import javax.jws.WebMethod; import javax.jws.WebParam; @WebService(serviceName = "StringOpsSOAP") @Stateless() public class StringOpsSOAP { @EJB private StringOps ops; @WebMethod(operationName = "echoString") public String echoString(@WebParam(name = "str") String str) { return ops.echoString(str); } @WebMethod(operationName = "reverseString") public String reverseString(@WebParam(name = "str") String str) { return ops.reverseString(str); } } 

Você precisa adicionar um ao seu web.xml que descreve quem tem permissão para acessar um determinado URL. Ele é descrito em Protegendo Aplicativos da Web no Tutorial do Java EE 6 . No seu caso, deveria ser algo assim:

  WebServiceSecurity  Authorized users only /yoururl POST   user admin  

Edit: Isto deve fazer o truque se você tiver feito o seu bean de session stateless um serviço web, adicionando a anotação @webservice à class e publicando os methods usando @webmethod , como dma_k disse nos comentários.

Editar 2: no link acima:

Especificando Restrições de Segurança

Uma restrição de segurança é usada para definir os privilégios de access a uma coleção de resources usando seu mapeamento de URL.

Mais para frente:

Especificando uma coleção de resources da Web

url-pattern é usado para listar o URI de solicitação a ser protegido. Muitos aplicativos têm resources desprotegidos e protegidos. Para fornecer access irrestrito a um recurso, não configure uma restrição de segurança para esse URI de solicitação específico.

E:

Especificando uma restrição de autorização

Uma restrição de autorização estabelece um requisito para autenticação e nomeia as funções autorizadas para acessar os padrões de URL e os methods HTTP declarados por essa restrição de segurança. Se não houver restrição de autorização, o contêiner deverá aceitar a solicitação sem exigir autenticação do usuário.

Portanto, nenhuma => no authentication => nenhuma function disponível.

Use informações de auditoria ou leitura ( getUserPrincipal() , isUserInRole() ) de WebServiceContext , para verificar isso.