|
About the attached code I provide no gaurantee or warantee (expressed or implied) about the code that comes with this blog entry; use it at your own risk. The attached zip file contains helloworld-ear.ear file and helloworld-client.zip file. The ear file contains ejb code in an ejb-jar file and http router code in a war file. Importing the ear file in RAD7 will automatically create necessary projects. The helloworld-client.zip file contains the client code to test the web service. You should be able to import the zip file as project interchange file in RAD7. If you have any questions, let me know. Summary I will be using WebSphere Application Server (WAS) v6.1 and Apache Directory Server (Apache DS) for this example. Also, to keep this article short and simple, I will not go into the details of how to create EJB, how to configure security, how to generate client etc. Instead, I have attached the code, which you can play with. If you need more details on the steps I have skipped, let me know. Here is the summary of the steps followed by more details: - Configure security on the server
- Create an ejb, expose it as a web service and configure security on service endpoint interface
- Generate http router servlet and configure http basic authentication
- In WAS admin console, map the application roles to ldap group
- Generate a web service client, set username and password and test
Configure security on the server I have explained WAS and Apache DS security configuration in my blogs Create an EJB, expose it as a web service and configure security on service endpoint interface The bean class is available at helloworld-ear.ear\helloworld-ejb.jar\com\taraba\security\ejb\ : package com.taraba.security.ejb; public class HelloWorldBean implements javax.ejb.SessionBean { public String sayHello (String name){ System.out.println ("HelloWorldBean#sayHello() was called by " +name); System.out.println ("HelloWorldBean#sayHello() user: " +mySessionCtx.getCallerPrincipal().getName()); System.out.println("HelloWorldBean#sayHello() isCallerInRole: " +mySessionCtx.isCallerInRole("ValidUser")); return "Hello World " + name; } private javax.ejb.SessionContext mySessionCtx; // other standard ejb methods avoided for simplicity | The sayHello() method prints a log statement, prints the username of the user calling the bean and whether the user is in ValidUser role. Finally, the method returns the string "Hello World" appended with the name supplied to the method. The EJB has already been exposed as a web service. Its WSDL file is available in the supplied ear file at helloworld-ear.ear\helloworld-ejb.jar\META-INF\wsdl\HelloWorld.wsdl. Role based security configuration can be seen in helloworld-ear.ear\META-INF\application.xml <security-role> <description> A user that can invoke methods on Hello World EJB</description> <role-name>ValidUser</role-name> </security-role> | And in helloworld-ear.ear\helloworld-ejb.jar\META-INF\ejb-jar.xml ... <security-role-ref> <role-name>ValidUser</role-name> <role-link>ValidUser</role-link> </security-role-ref> ... <assembly-descriptor> <security-role> <description> A user that can invoke methods on Hello World EJB</description> <role-name>ValidUser</role-name> </security-role> <method-permission> <role-name>ValidUser</role-name> <method> <ejb-name>HelloWorld</ejb-name> <method-intf>Remote</method-intf> <method-name>sayHello</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </method> <method> <ejb-name>HelloWorld</ejb-name> <method-intf>ServiceEndpoint</method-intf> <method-name>sayHello</method-name> <method-params> <method-param>java.lang.String</method-param> </method-params> </method> </method-permission> </assembly-descriptor> | The security configuration indicates that the sayHello() method of the EJB can only be invoked by the users in ValidUser role. This security configuration applies to remote and service clients (see the bold parts in the xml fragment shown above). Also review the security-role-ref section. This section is required since we are calling the isCallerInRole () method. The security configuration described above is explained in ejb 2.x specification. Defining security configuration of sayHello() method exposed by the Service Endpoint Interface makes the method secure for web service clients. If the web service client is not in ValidUser role, as defined in the <method-permission> tag, UnauthorizedException will be thrown. Generate http router servlet and configure HTTP BASIC authentication The authentication configuration is configured in helloworld-ear.ear\helloworld-http-router.war\WEB-INF\web.xml <security-constraint> <display-name>ws_constraint</display-name> <web-resource-collection> <web-resource-name>ws_resource</web-resource-name> <url-pattern>/services/HelloWorld</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <description>Valid User</description> <role-name>ValidUser</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>web service basic authentication</realm-name> </login-config> <security-role> <description>web services user</description> <role-name>ValidUser</role-name> </security-role> | As the bold parts of the fragment above indicate, the /services/HelloWorld url is protected for ValidUser role. The users will be authenticated using BASIC authentication. In WAS admin console, map the security roles to LDAP groups - so that the user information can be retrieved from LDAP In configure security on the server section, I described how to create users and groups in LDAP. We will map the ValidUser role in the application to the endusers group in LDAP using WAS admin console as shown below: Generate a web service client and set username and password I generated a java client from the wsdl file and packaged it in a jar file helloworld-client.jar. I then wrote a com.taraba.security.ejb.client.HelloWorldClient class with a main method as shown below: public static void main(String[] args) { HelloWorldProxy proxy = new HelloWorldProxy (); try { System.out.println (proxy.sayHello("Taraba Consulting")); } catch (RemoteException e) { e.printStackTrace(); } } | As shown above, the method simply instantiates the proxy, calls sayHello method on it and prints the result. Running the client as is will return a soap fault with Unauthorized error. To fix this, uncomment the following two lines in com.taraba.security.ejb.HelloWorldProxy#sayHello().
// ((javax.xml.rpc.Stub)__helloWorld)._setProperty(Stub.USERNAME_PROPERTY, "bob");
// ((javax.xml.rpc.Stub)__helloWorld)._setProperty(Stub.PASSWORD_PROPERTY, "password"); | As you can see above, the usename and password can be set by populating USERNAME_PROPERTY and PASSWORD_PROPERTY defined in javax.xml.rpc.Stub class. If you are invoking the web service using Dynamic Invocation, you can set these properties in javax.xml.rpc.Call class. If you run the code after uncommenting those lines, you will see the following output, HelloWorldBean#sayHello() was called by Taraba Consulting HelloWorldBean#sayHello() user: bob HelloWorldBean#sayHello() isCallerInRole: true Recap So, let's just recap what we did. We configured security on the ejb that was exposed as a web service requiring the client to be in ValidUser role. We configured security in the router servlet by protecting the url /services/HelloWorld - the url that would be used by the client to invoke the service. Again, we allowed only ValidUser to access the url. We mapped ValidUser role to the enduser group that I had already defined in LDAP. We set username and password of a user that belongs to enduser group in LDAP. Before you decide to use this method for web service authentication and authorization, I encourage you to review A word of caution section in my previous blog entry.
|