Sunday 21 September 2014

Restful Webservices security example using Apache cxf , Spring web security and Jboss 5.0.1

REST (Representational State Transfer) is an architecture style that describes how to use HTTP to access web services.
In my previous blog I have explained with example how to create simple RESTful webservice application.
Restful Web Services example using CXF , JAXRS.
This Blog instruction will help us to create a sample secure RESTful webservice using cxf , spring security api and jaxrs api.
NB. I choose spring xml security configuration to secure RESTful webservice application so it will be easy for user to switch on/off and configure security features without modifying service interface and implementation classes.

  • Required Software
  • Pre-requsite
  • Instructions assume that jdk,jboss,cxf is installed without any error.

  • Instructions
    1. please open existing project that successfully created by using previous blog instructions previous blog instructions . Now we will modify existing project and secure the web application.

    2. Open web.xml under WEB-INF\web.xml and add following xml snippet.


    3. <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.5" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee           http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
          <display-name>jaxrs-security-example</display-name>
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>WEB-INF/conf/*beans.xml</param-value>
          </context-param>
          <listener>
              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
          <filter>
              <filter-name>springSecurityFilterChain</filter-name>
              <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
          </filter>
          <filter-mapping>
              <filter-name>springSecurityFilterChain</filter-name>
              <url-pattern>/*</url-pattern>
          </filter-mapping>
          <servlet>
              <description>Apache CXF Endpoint</description>
              <display-name>cxf</display-name>
              <servlet-name>CXFServlet</servlet-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>
      


    4. Create a new package "com.kaustuv.jaxrs.example.security"

    5. create a new customize exception class NotAuthorizedException under package "com.kaustuv.jaxrs.example.security". Add following code snippet.

    6. /**
      |
      | Copyright (c) Kaustuv Maji , 2013
      | 
      | Please do not use source code in production.
      | Repos -  https://github.com/kaustuvmaji
      | Blog  -  http://kaustuvmaji.blogspot.in 
      */
      package com.kaustuv.jaxrs.example.security;
      
      import javax.ws.rs.WebApplicationException;
      import javax.ws.rs.core.MediaType;
      import javax.ws.rs.core.Response;
      import javax.xml.ws.WebFault;
      
      /**
       * @author KMaji
       *
       */
      @WebFault
      public class NotAuthorizedException extends WebApplicationException{
      
        private static final long serialVersionUID = -1203116970226591712L;
      
      
        public NotAuthorizedException(String faultString) {
          super(Response.status(Response.Status.UNAUTHORIZED)
                .entity(faultString).type(MediaType.TEXT_PLAIN).build());
          System.out.println("################NotAuthorizedException(String faultString)######################");
        }
      
      }
      

    7. Create SecurityExceptionMapper under package "com.kaustuv.jaxrs.example.security". Add following code snippet

    8. /**
      |
      | Copyright (c) Kaustuv Maji , 2014
      |
      | Please do not use source code in production.
      | Repos -  https://github.com/kaustuvmaji
      | Blog  -  http://kaustuvmaji.blogspot.in
      */
      package com.kaustuv.jaxrs.example.security;
      
      import javax.ws.rs.core.Response;
      import javax.ws.rs.ext.ExceptionMapper;
      import javax.ws.rs.ext.Provider;
      
      import com.kaustuv.jaxrs.example.security.NotAuthorizedException;
      /**
       *
       * @author kmaji
       */
      @Provider
      public class SecurityExceptionMapper implements ExceptionMapper {
        public Response toResponse(NotAuthorizedException exception) {
          Response.Status status;
          // This means that the client could not be authenticated. In this case the client may want to
          // send (new) credentials and we should return 401.
          status = Response.Status.UNAUTHORIZED;
          return Response.status(status).entity(exception.getLocalizedMessage()).build();
        }
      
      }
      

    9. Open global-beans.xml under WEB-INF/conf and add bean following line to create SecurityExceptionMapper bean.
    10. <bean id="exceptionProvider" class="com.kaustuv.jaxrs.example.security.SecurityExceptionMapper" />
      
      We will use this exceptionProvider bean in our jaxrs server (global) configuration so all services will be able to use this bean.
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:security="http://www.springframework.org/schema/security"
      
       xsi:schemaLocation=" http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/security
       http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
       http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
       <import resource="classpath:META-INF/cxf/cxf*.xml" />
       <!-- Cxf Jason provider -->
          <bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.JSONProvider">
              <property name="dropRootElement" value="true" />
              <property name="supportUnwrapped" value="true" />
          </bean>
          <!-- Cxf Jaxb Provider -->
          <bean id="jaxbXmlProvider" class="org.apache.cxf.jaxrs.proviacder.JAXBElementProvider" scope="prototype" />
          <!-- cxf exception generator -->
          <bean id="exceptionProvider" class="com.kaustuv.jaxrs.example.security.SecurityExceptionMapper" />
          <!-- Cxf Cors filter (cross domain purpose)-->
          <bean id="cors-filter" class="org.apache.cxf.jaxrs.cors.CrossOriginResourceSharingFilter"/>
      </beans>
      

    11. Open jaxrs-beans.xml under WEB-INF/conf and refer exceptionProvider bean information in the jaxrs:providers section along with existing jsonProvider,jaxbXmlProvider,cors-filter

    12. <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:jaxrs="http://cxf.apache.org/jaxrs"
          xmlns:cxf="http://cxf.apache.org/core"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xmlns:security="http://www.springframework.org/schema/security"
          xsi:schemaLocation=" http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/aop
          http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/security
          http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
          http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
          <import resource="classpath:META-INF/cxf/cxf*.xml"/>
      
          <!-- Service -->
          <bean id="customerService" class="com.kaustuv.jaxrs.example.service.DemoCustomerServiceImpl"
          init-method="init"
          destroy-method="destroy" />
      
          <!-- Jaxrs Server -->
          <jaxrs:server id="restserver" address="/customerService">
              <!-- adding service bean -->
              <jaxrs:serviceBeans>
                  <ref bean="customerService" />
              </jaxrs:serviceBeans>
              <!-- adding media type provider -->
              <jaxrs:providers>
                  <ref bean="jsonProvider" />
                  <ref bean="jaxbXmlProvider" />
                  <ref bean="cors-filter" />
                  <ref bean="exceptionProvider"/>
              </jaxrs:providers>
               <jaxrs:features>
                  <cxf:logging/>
              </jaxrs:features>
              <!-- Keeping extention type -->
              <jaxrs:extensionMappings>
                  <entry key="json" value="application/json" />
                  <entry key="xml" value="application/xml" />
                  <entry key="html" value="text/html"/>
                  <entry key="feed" value="application/atom+xml"/>
              </jaxrs:extensionMappings>
          </jaxrs:server>
      </beans>
      

    13. Create a xml name security-beans.xml under WEB-INF/conf
    14. <?xml version="1.0" encoding="UTF-8"?>
      <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
          http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
       <context:component-scan base-package="com.kaustuv.jaxrs.example" />
       <!-- cxf exception generator -->
       <beans:bean id="exceptionProvider" class="com.kaustuv.jaxrs.example.security.SecurityExceptionMapper" />
       <!-- Authentication manager configuration -->
       <authentication-manager alias="security">
        <authentication-provider>
         <user-service>
          <user name="kaustuv" password="kaustuv" authorities="ROLE_ADMIN" />
          <user name="get" password="get" authorities="ROLE_GET" />
          <user name="post" password="post" authorities="ROLE_POST" />
          <user name="put" password="put" authorities="ROLE_PUT" />
          <user name="delete" password="delete" authorities="ROLE_DELETE" />
         </user-service>
        </authentication-provider>
       </authentication-manager>
       <!-- HTTP basic authentication in Spring Security [option 1] -->
       <!--
         <http>
         <intercept-url pattern="/customerService/*" access="ROLE_ADMIN" />
         <http-basic />
         </http>
        -->
       <!-- HTTP basic authentication in Spring Security with http intercept url pattern [Option 2] -->
       <http create-session="stateless" use-expressions="true">
        <intercept-url pattern="/customerService/getCustomerById/**" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/customerService/addCustomer/**" access="hasRole('ROLE_POST')" />
        <intercept-url pattern="/customerService/updateCustomer/**" access="hasRole('ROLE_PUT')" />
        <!-- for multiple authorities use hasAnyRole -->
        <intercept-url pattern="/customerService/deleteCustomer/**" access="hasAnyRole('ROLE_ADMIN','ROLE_DELETE')" />
        <http-basic />
       </http>
      </beans:beans>
      
      Now in above xml we are protecting each resource path with the access role. Suppose we want to add customer then we have to use username=post and password=post to add customer otherwise we will not be able to access the resource path. Now carefully look in the authentication manager configuration section, there we configured authorities based on the username and password and in option 2 section we are saying that this path will be access by authenticated users. We can allow anonymous users to access any path by just providing permitAll.
       <intercept-url pattern="/" access="permitAll"/>
      
      In the above xml there is another section that is option 1. Please note that it is not allowed to keep both option open. Now if we use option 1 then we have to remove or commented out option 2 in the xml then we will uncomment the option 1 section . So by using option 1 each and every methods will be accessible by just providing username=kaustuv and password=kaustuv. In the post developement testing chapter we will the example snippets.
    15. Export and package this as war. Destination path %JBOSS_HOME%\server\default


    16. Open command prompt and go to directory %JBOSS_HOME\bin .
    17. Execute run.bat -c default -b 0.0.0.0
    18. click on our wadl url for our restful webservice project.
    19. http://localhost:8080/jaxrs-security-example/customerService?_wadl

        Post Development testing

    1. open soap ui and create a new project using our wadl url.
    2. http://localhost:8080/jaxrs-security-example/customerService?_wadl

      Test HTTP POST Method
    3. Explore soap ui project name RestCustomerService. Select Request of service addCustoer
    4. click on auth tab and configure username , password as a new Basic type Authorization . username and password will be post/post.

    5. enter customer xml use following snippet and click on submit .
    6. <customer>
      <customerId>3</customerId>
      <customerName>testPost</customerName>
      <phoneNumber>9831098301</phoneNumber>
      <address>
      <street>saltlake</street>
      <city>kolkata</city>
      <state>westbengal</state>
      <country>India</country>
      <pincode>700064</pincode>
      </address>
      </customer>

       



      Test HTTP PUT Method
    7. Explore soap ui project name RestCustomerService. Select Request of service updateCustomer
    8. click on auth tab and configure username , password as a new Basic type Authorization . username and password will be put/put.
    9. enter customer xml use following snippet and click on submit .

    10. <customer>
      <customerId>3</customerId>
      <customerName>testUpdate</customerName>
      <phoneNumber>9831098301</phoneNumber>
      <address>
      <street>saltlake</street>
      <city>kolkata</city>
      <state>westbengal</state>
      <country>India</country>
      <pincode>700064</pincode>
      </address>
      </customer>

       


      Test HTTP GET Method
    11. Explore soap ui project name RestCustomerService. Select Request of service getCustomerById
    12. click on auth tab and configure username , password as a new Basic type Authorization . username and password will be kaustuv/kaustuv.
    13. enter value of parameter custId in following example picture value is 2 . click on submit

    14. Test HTTP DELETE Method
    15. Explore soap ui project name RestCustomerService. Select Request of service deleteCustomer
    16. click on auth tab and configure username , password as a new Basic type Authorization . username and password will be delete/delete.
    17. enter value of parameter custId in following example picture value is 3 . click on submit .



    Click here to download source code of above example

    Source Code


    • References:
    CXF documents

    European Union laws require you to give European Union visitors information about cookies used on your blog. In many cases, these laws also require you to obtain consent.

    As a courtesy, we have added a notice on your blog to explain Google's use of certain Blogger and Google cookies, including use of Google Analytics and AdSense cookies.

    No comments:

    Post a Comment