Monday, March 4, 2013

Webdav example in Spring Security with password encription

You can find few set of link which will guide you to do the spring configuration for the webdav. But, i have reallized that most of the readers getting trouble when implementing those codes as they are not properly explain how to do it basic level.

Here is the clear working guide for the spring webdav integration with encripted password.

01. Setup the spring blank project from your desired developement tool (netbeans, eclipse,intelliJ IDEA). I am using intelliJ on this tutorial. And setup spring security example based on the available sources on the web.
Here is the few links you may try.
http://blog.solidcraft.eu/2011/03/spring-security-by-example-set-up-and.html
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/sample-apps.html

After completing the above steps, you could be able to log in to the web application using form based authontification with encripted password on the database.

Now lets setup for the webdav folder access for the above web project.

02. Modify the web.xml file for the webdav configuration.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <servlet>
        <servlet-name>webdav</servlet-name>
        <servlet-class>org.apache.catalina.servlets.WebdavServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>true</param-value>
        </init-param>
        <!-- Read-Write Access Settings -->
        <init-param>
            <param-name>readonly</param-name>
            <param-value>false</param-value>
        </init-param>
    </servlet>
    <!-- URL Mapping -->
    <servlet-mapping>
        <servlet-name>webdav</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/application-security.xml
            /WEB-INF/applicationContext.xml
        </param-value>
    </context-param>
    <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>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>
03. As you can see the above web.xml file, following two spring configuration file should be available.

            /WEB-INF/application-security.xml
            /WEB-INF/applicationContext.xml

04. application-security.xml file is use to control the spring security for this example.

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
             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-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <http>
        <intercept-url pattern="/**" access="ROLE_DESKTOP_USER"/>
        <http-basic/>
        <custom-filter ref="digestFilter" after="BASIC_AUTH_FILTER"/>
    </http>

    <beans:bean id="digestFilter" class="org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
        <beans:property name="userDetailsService" ref="myAuthenticationProvider"/>
        <beans:property name="authenticationEntryPoint" ref="digestEntryPoint"/>
        <beans:property name="passwordAlreadyEncoded" value="true"></beans:property>
    </beans:bean>
     <beans:bean id="digestEntryPoint"
                class="org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
                <beans:property name="realmName" value="my test" />
                <beans:property name="key" value="test" />
                <beans:property name="nonceValiditySeconds" value="10" />
        </beans:bean>


    <authentication-manager>
        <authentication-provider user-service-ref='myAuthenticationProvider'>
             <password-encoder ref="passwordEncoder">
                 <salt-source ref="DigestAuthenticationAwareSaltSource"/>
             </password-encoder>
        </authentication-provider>
    </authentication-manager>

      <beans:bean id="digestAuthenticationProvider"
                class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
                <beans:property name="userDetailsService" ref="myAuthenticationProvider" />
        </beans:bean>

     <beans:bean id="DigestAuthenticationAwareSaltSource" class="com.indika.service.DigestAwarePasswordEncoder">
         <beans:property name="digestRealm" value="Contacts Realm via Digest Authentication"/>
    </beans:bean>


    <beans:bean class="com.indika.service.DigestAuthenticationAwarePasswordEncoder" id="passwordEncoder"/>

    <beans:bean id="myAuthenticationProvider" class="com.indika.service.UserDetailsServiceImpl">
        <beans:property name="mstuserdao" ref="mstuserdao"/>
        <beans:property name="mstusergroupdao" ref="mstusergroupdao"/>
    </beans:bean>

    <beans:bean id="mstuserdao" class="com.indika.repository.MstUserDAO">
        <beans:property name="hibernateTemplate">
            <beans:ref bean="hibernateTemplate"/>
        </beans:property>
    </beans:bean>

    <beans:bean id="mstusergroupdao" class="com.indika.repository.MstUserGroupDAO">
        <beans:property name="hibernateTemplate">
            <beans:ref bean="hibernateTemplate"/>
        </beans:property>
    </beans:bean>

</beans:beans>


Above red colour classes should be created by you.

05. DigestAuthenticationAwarePasswordEncoder.java

package com.indika.service;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;


public class DigestAuthenticationAwarePasswordEncoder implements SaltSource, InitializingBean {

    private String digestRealm;


    public void afterPropertiesSet() throws Exception {
        Assert.hasText(digestRealm, "A Digest Realm must be set");
    }


    public Object getSalt(UserDetails user) {
        return String.format("%s:%s:", user.getUsername(), digestRealm);
    }

    public void setDigestRealm(String digestRealm) {
        this.digestRealm = digestRealm;
    }
}

06. DigestAwarePasswordEncoder.java


package com.indika.service;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;


public class DigestAwarePasswordEncoder implements SaltSource, InitializingBean {

    private String digestRealm;


    public void afterPropertiesSet() throws Exception {
        Assert.hasText(digestRealm, "A Digest Realm must be set");
    }


    public Object getSalt(UserDetails user) {
        return String.format("%s:%s:", user.getUsername(), digestRealm);
    }

    public void setDigestRealm(String digestRealm) {
        this.digestRealm = digestRealm;
    }
}




07. UserDetailsServiceImpl.java/MstUserDAO.java/MstUserGroupDAO.java

You can find many sources for the above classes from the web related to the spring security configuration.



08. Once every things were completed, you can login webdav folder using the encripted password.



3 comments:

  1. You included DigestAwarePasswordEncoder twice - once it should be DigestAuthenticationAwarePasswordEncoder, which is missing

    ReplyDelete
    Replies
    1. Thanks for pointing out the mistake. Now it should ok. Thanks again..

      Delete
  2. Hi Indika,

    I have followed the above steps to configure digest auth for my Web Service but in the request header I am always getting "Headers: {Accept=[*/*], Authorization=[Basic Ym9iOmJvY...."

    client code ---

    public static void main(String args[]) throws Exception {

    JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
    factory.setServiceClass(NextOrderNumber.class);
    factory.setAddress("http://localhost:5050/ws/NextOrderNumberService");

    Map outProps = new HashMap();
    outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
    outProps.put("passwordType", "PasswordDigest");
    outProps.put(WSHandlerConstants.USER,"abcd");
    outProps.put("password", "dcba");

    outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());

    WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor(outProps);
    factory.getOutInterceptors().add(wssOut);

    NextOrderNumber client = (NextOrderNumber) factory.create();
    String nextOrderNumber = client.getNextOrderNumber();
    System.out.println("NextOrderNUmber is " + nextOrderNumber);
    System.exit(0);

    }

    ----- spring config file


























    Could you please assist me ? Please post the client code as well ?

    Many thanks

    ReplyDelete