Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
412 views
in Technique[技术] by (71.8m points)

java - Unable to authenticate with jax-ws on Glassfish

I have been trying to create a java service that uses basic authentication over SSL. Configuring the SSL was quite simple but configuring authentication is much more of a problem. Any time I try to call a method marked with a @RolesAllowed annotation I receive the following exception...

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)

My server is using JavaEE on Glassfish 3. It's functions are defined in a bean as follows...

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);
    }
}

I have added the login config to my web.xml file...

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>testing-frealm</realm-name>
</login-config>
<security-role>
    <description/>
    <role-name>admin</role-name>
</security-role>
<security-role>
    <description/>
    <role-name>user</role-name>
</security-role>

The security realm is just a file realm with one user(name: "andrew", pass: "12345"). I've also added the role-group mappings to my glassfish-web.xml

<security-role-mapping>
    <role-name>user</role-name>
    <group-name>user</group-name>
</security-role-mapping>
<security-role-mapping>
    <role-name>admin</role-name>
    <group-name>admin</group-name>
</security-role-mapping>

On the client side, I have a simple swing application to test from...

Swing Test Application

The call being made is to the reverse function on a SOAP channel. The SOAP client components are generated with NetBeans via the New->WebService Client function. And the action associated with the reverse button click is...

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);
}

I'm sure there is just some little thing that I'm missing, but I've been looking at the problem for days and still haven't found it.

-- Edit -- I realized there might be some confusion since I didn't post my WebService code. The EJB that contains the String functions is accessed through a 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);
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You need to add a <security-constraint> to your web.xml which describes who is allowed to access a certain URL. It's described in Securing Web Applications in the Java EE 6 Tutorial. In your case it should be something like this:

<security-constraint>  
<display-name>WebServiceSecurity</display-name>  

<web-resource-collection>  
    <web-resource-name>Authorized users only</web-resource-name>  
    <url-pattern>/yoururl</url-pattern>  
    <http-method>POST</http-method>
</web-resource-collection>  

<auth-constraint>       
    <role-name>user</role-name>
    <role-name>admin</role-name>
</auth-constraint>  

Edit: This should do the trick if you have make your stateless session bean a web service, by adding the @webservice annotation to the class and publishing the methods using @webmethod, as dma_k said in comments.

Edit 2: From the link above:

Specifying Security Constraints

A security constraint is used to define the access privileges to a collection of resources using their URL mapping.

Further on:

Specifying a Web Resource Collection

url-pattern is used to list the request URI to be protected. Many applications have both unprotected and protected resources. To provide unrestricted access to a resource, do not configure a security constraint for that particular request URI.

And:

Specifying an Authorization Constraint

An authorization constraint establishes a requirement for authentication and names the roles authorized to access the URL patterns and HTTP methods declared by this security constraint. If there is no authorization constraint, the container must accept the request without requiring user authentication.

So, no <security-constraint> => no authentication => no roles available.

Use auditing or read info (getUserPrincipal(), isUserInRole()) from WebServiceContext, to verify this.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...