2
votes

Desperately need help here securing a simple Apache CXF web service. Attempts with Spring Security is taking me no where so I need to find a different strategy. This is to implement authorization on a legacy Java service implemented for some of our clients.

This simple Apache CXF web service was created using Maven's cxf-jaxws-javafirst prototype. It produced a web.xml and beans.xml file and sample code. Besides beans.xml which remains in default state, I have modified these entities as follows:

web.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">


<web-app>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/beans.xml</param-value>
    </context-param>
     <context-param>
     <param-name>shiroConfigLocations</param-name>
        <param-value>WEB-INF/shiro.ini</param-value>
      </context-param>  

     <filter>
        <filter-name>ShiroFilter</filter-name>
        <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>       
    </filter>

    <filter-mapping>
        <filter-name>ShiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>      
        <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
    </listener> 

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>      
    </listener>


    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <display-name>CXF Servlet</display-name>
        <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

and my Shiro.ini file looks like this:

# =======================
# Shiro INI configuration
# =======================

[main]
authc = org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

[users]
o = o, OPERATOR
a = a, ADMIN
s = s, SUPERVISOR

[roles]
SUPERVISOR = *
ADMIN = sayHiAdmin
OPERATOR = deleteAccounts

My simple webservice code is as follows:

import javax.jws.WebService;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;

@WebService(endpointInterface = "org.myCo.com.CxfShiroSecuredService.HelloWorld")
public class HelloWorldImpl implements HelloWorld {

    public String sayHi(String text) {              

        if (isAuthorized("sayHi")) {
            return "Successfully said hi " + text;
        }               

        if (hasRole("OPERATOR")){
            return "User is OPERATOR";
        }
        if (hasRole("ADMIN")){
            return "User is OPERATOR";
        }
        throw new UnauthorizedException("Logged user does not have OPERATOR's permission");                             
    }    

    public String sayHiAdmin(String text) {         

        if (isAuthorized("sayHiAdmin")) {
            return "Successfully said hi Admin " + text;
        }               

        throw new UnauthorizedException("Logged user does not have ADMIN permission");
    }

    public String deleteAccounts(String text) {             

        if (isAuthorized("deleteAccounts")) {
            return "Successfully deleted accounts " + text;
        }               

        throw new UnauthorizedException("Logged user does not have SUPERVISOR permission");
    }

    private Boolean isAuthorized(String operation){
        Subject currentUser = SecurityUtils.getSubject();       
        return currentUser.isPermitted(operation);  //currentUser.isAuthenticated(); // && currentUser.isPermitted(operation);      
    }

    private Boolean hasRole(String role){
        Subject currentUser = SecurityUtils.getSubject();       
        return currentUser.hasRole(role);       
    }
}

I have a C# test client that passes authentication information in the SOAP header before invoking webservice like so:

 private void OnButtonClick(object sender, RoutedEventArgs e)
        {
            var client = new HelloWorldClient();
            var response = "";

            using (new OperationContextScope(client.InnerChannel))
            {
                var httpRequestProperty = new HttpRequestMessageProperty();
                httpRequestProperty.Headers[System.Net.HttpRequestHeader.Authorization] = "Basic " +
                Convert.ToBase64String(Encoding.ASCII.GetBytes(UserName.Text + ":" + Password.Text));
                OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;

                try
                {
                    response = client.sayHi("hi " + UserName.Text);
                }
                catch (TimeoutException tex)
                {
                    response = tex.Message;
                }
                catch (CommunicationException cex)
                {
                    response = cex.Message;
                }
            }

            TextBox.Text = response;

        }

I have used this same strategy for other web services that require Basic authentication before invoking method calls with success but this service does not seem to be recognizing my credentials. For each method call invoked, regardless of username/password combination, I get the UnAuthorizedException thrown. Can someone shed me some light?

Thanks in advance.

1

1 Answers

2
votes

You need a [urls] section in your shiro.ini file. Something like this:

[urls]
/** = authc

Check out the documentation for further details here.