JAX-WS Security Example
In this example we shall learn, how to implement JAX-WS security to SOAP web services.
Security is always critical to web services. When talking about web services security here, following security issues are considered:
Wire-level security
- Assurance between client and web service that they are the only one communicating.
- Data encryption.
- Assurance that received message is same as sent message.
User authentication and authorization
- Authentication is appropriate credentials to gain access.
- Authorization is users-role security. Users might be restricted to some resources based on their roles.
1. Wire-level security using HTTPS
To make web service more secure, we can use HTTPS instead of HTTP. It addresses three security services
over transport services that HTTP provides; Peer authentication, confidentiality, and integrity.
2. Container managed security for web service
2.1 Deploying web service under tomcat
To understand this let us first create a ‘Dynamic Web Project’ in eclipse.
Now we shall implement Service Endpoint Interface as follows:
CalculatorI.java
package com.javacodegeeks.jaxws.example; import javax.jws.WebMethod; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; @WebService @SOAPBinding(style = Style.RPC) public interface CalculatorI { @WebMethod int add(int a, int b); @WebMethod int subtract(int a, int b); @WebMethod int multiply(int a, int b); @WebMethod int divide(int a, int b); }
After this we shall implement Service Implementation Bean as follows:
CalculatorImpl.java
package com.javacodegeeks.jaxws.example; import javax.jws.WebService; @WebService(endpointInterface = "com.javacodegeeks.jaxws.example.CalculatorI") public class CalculatorImpl implements CalculatorI { @Override public int add(int a, int b) { return a + b; } @Override public int subtract(int a, int b) { return a - b; } @Override public int multiply(int a, int b) { return a * b; } @Override public int divide(int a, int b) { return a / b; } }
To configure a container like tomcat to host a web service, we need to add configuration of JAX-WS’s WSServlet
and WSServletContainerListener
in web.xml
. web.xml
on configuration shall be like:
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>JaxWSSecurityExample</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>CalculatorWS</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CalculatorWS</servlet-name> <url-pattern>/calc</url-pattern> </servlet-mapping> <listener> <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class> </listener> </web-app>
Now to add the endpoint that we created to be hosted by tomcat, we will create sun-jaxws.xml
in WEB-INF directory as follows:
sun-jaxws.xml
<?xml version="1.0" encoding="UTF-8"?> <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0"> <endpoint name="CalcWS" implementation="com.javacodegeeks.jaxws.example.CalculatorImpl" /> </endpoints>
2.2 Securing web service under tomcat
Next step is to secure the web service or to enable https. To do this, go to Tomcat’s conf directory and edit server.xml
file.
But first things first, we will need to create a digital certificate. We can use Java’s keytool utility to generate the same. The command would be like: keytool -genkey -alias tomcat -keyalg RSA
By default a digital certificate file with name .keystore
shall be created in the user’s home directory. Now to configure this file to enable https, we shall edit connector configuration in server.xml
as mentioned above. The new configuration shall be like:
server.xml (partial)
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="/home/saurabharora123/.keystore" />
2.3 Implementing authentication and authentication
Next step in securing our web service is to implement authentication and authorization. This can be done at at either application level or container level. We will look at each of these methods.
2.3.1 Authentication at application level
Authentication if done at application level is easy but creates clumsy and un-readable code. The change to web service would be minor.
CalculatorImplAppManagedAuth.java
package com.javacodegeeks.jaxws.example; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.jws.WebService; import javax.xml.ws.WebServiceContext; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.http.HTTPException; @WebService(endpointInterface = "com.javacodegeeks.jaxws.example.CalculatorI") public class CalculatorImplAppManagedAuth implements CalculatorI { @Resource WebServiceContext context; @Override public int add(int a, int b) { if (isAuthenticated()) return a + b; else throw new HTTPException(401); } @Override public int subtract(int a, int b) { if (isAuthenticated()) return a - b; else throw new HTTPException(401); } @Override public int multiply(int a, int b) { if (isAuthenticated()) return a * b; else throw new HTTPException(401); } @Override public int divide(int a, int b) { if (isAuthenticated()) return a / b; else throw new HTTPException(401); } private boolean isAuthenticated() { MessageContext messageContext = context.getMessageContext(); Map httpHeaders = (Map) messageContext.get(MessageContext.HTTP_REQUEST_HEADERS); List userNameList = (List) httpHeaders.get("uname"); List passwordList = (List) httpHeaders.get("pass"); if (userNameList.contains("saurabh") && passwordList.contains("java")) return true; else return false; } }
In the above program, username and password are expected in http headers which are then authenticated. In case request is not authenticated HTTPException
with code 401
shall be thrown which is for unauthorized access. The above program is just a sample, in the real-world scenario this kind of authentication can be done form databases or LDAP or other such repositories. On similar pattern authorization can be implemented.
The downside of the above approach is that the logic is now a mix of application logic and security implementation.
To compliment this web service client shall have to do some additional line of code to put username and password in the HTTP headers.
2.3.2 Authentication and authorization managed by container
Implementing authentication and authorization managed by container like Tomcat is just a matter of some configuration.
First step is to edit the web.xml to implement security constraints.
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>JaxWSSecurityExample</display-name> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>CalculatorWS</servlet-name> <servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CalculatorWS</servlet-name> <url-pattern>/calc</url-pattern> </servlet-mapping> <listener> <listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class> </listener> <security-role> <description>Admin role</description> <role-name>admin</role-name> </security-role> <security-constraint> <web-resource-collection> <web-resource-name>UserRoleSecurity</web-resource-name> <url-pattern>/calc</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> </login-config> </web-app>
In this updated web.xml
resources to be updated are mentioned in web-resource-collection
tag. role-name
tag describes the role which authenticated user should have. Here we are using BASIC
authentication. Transport is guaranteed to be CONFIDENTIAL
which covers services of authentication, encryption and message integrity.
When tomcat receives request, it will know that request needs to be authenticated and authorized. For verification of username, password and role, it shall look into MemoryRealm
by default which is configured in file conf/tomcat-users.xml
.
tomcat-users.xml
<tomcat-users> <role rolename="admin"/> <role rolename="normalUser"/> <user username="saurabh" password="java" roles="admin"/> <user username="both" password="tomcat" roles="tomcat,role1"/> <user username="role1" password="tomcat" roles="role1"/> </tomcat-users>
To use digested password auth-method
in web.xml
shall have to be changed to DIGEST
. The digested password can be generated using digest.sh
utility in tomcat’s bin directory. And then this digested password shall be replaced in tomcat-users.xml
.
3. Directory structure of this example
The directory structure of the above example in eclipse shall look like:
4. Download the source code
This was an example of JAX-WS security.
You can download the full source code of this example here: JaxWSSecurityExample
Could you please provide the client application of the same
Hi Saurabh,
Please provide the program at client side for above secure soap web service. Please provide the program to test above secure web services. I generated wsdl file for above web services using Endpoint.publish but still not able to authenticate above web service. It would be great if you provide client program to test above web services with authentication and soap request(xml). and find out proper soap response(xml) like postman and soap ui.