The title says it all. This post will show you how to configure Spring Security with Digest authentication and encoded password. But first, here is the Spring Security documentation about digest authentication and encoded password:

The configured UserDetailsService is needed because DigestAuthenticationFilter must have direct access to the clear text password of a user. Digest Authentication will NOT work if you are using encoded passwords in your DAO. The DAO collaborator, along with the UserCache, are typically shared directly with a DaoAuthenticationProvider. The authenticationEntryPoint property must be DigestAuthenticationEntryPoint, so that DigestAuthenticationFilter can obtain the correct realmName and key for digest calculations.
It's not all true. Let's take a look at the source code of DigestAuthenticationFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    // omit code for simplicity
    
    // Compute the expected response-digest (will be in hex form)
    String serverDigestMd5;

    // Don't catch IllegalArgumentException (already checked validity)
    serverDigestMd5 = DigestAuthUtils.generateDigest(passwordAlreadyEncoded, username, realm, user.getPassword(),
                    request.getMethod(), uri, qop, nonce, nc, cnonce);
    // omit code for simplicty
}

Digging a little more into the method generateDigest of the class DigestAuthUtils, we have:

static String generateDigest(boolean passwordAlreadyEncoded, String username, String realm, String password,
                                        String httpMethod, String uri, String qop, String nonce, String nc, String cnonce)
            throws IllegalArgumentException {
    String a1Md5 = null;
    String a2 = httpMethod + ":" + uri;
    String a2Md5 = md5Hex(a2);

    if (passwordAlreadyEncoded) {
        a1Md5 = password;
    } else {
        a1Md5 = DigestAuthUtils.encodePasswordInA1Format(username, realm, password);
    }
   
   // The rest of the code

A little more into encodePasswordInA1Format of DigestAuthUtils:

static String encodePasswordInA1Format(String username, String realm, String password) {
    String a1 = username + ":" + realm + ":" + password;
    String a1Md5 = md5Hex(a1);

    return a1Md5;
}
From here, if we set the property passwordAlreadyEncoded of DigestAuthenticationFilter to true and create a suitable password encoder and salt source, we can make Spring Security digest authentication work with our tailored password encoder, salt source. Let's create them:
package com.mycompany.myproject;

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 DigestAuthenticationAwareSaltSource implements SaltSource, InitializingBean {
 
    private String digestRealm;
 
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.hasText(digestRealm, "A Digest Realm must be set");  
    }

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

    public void setDigestRealm(String digestRealm) {
        this.digestRealm = digestRealm;
    }
}
package com.mycompany.myproject;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.codec.Hex;

public class DigestAuthenticationAwarePasswordEncoder implements PasswordEncoder {

    @Override
    public String encodePassword(String rawPass, Object salt) throws DataAccessException {
        String a1 = salt + rawPass;
        return md5Hex(a1);
    }

    @Override
    public boolean isPasswordValid(String encPass, String rawPass, Object salt) throws DataAccessException {
        String calculatedPass = md5Hex(salt + rawPass);
        return calculatedPass.equals(encPass);
    }
 
    private String md5Hex(String data) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("No MD5 algorithm available!");
        }

        return new String(Hex.encode(digest.digest(data.getBytes())));
    }
}
And now you can do Digest Authentication with encoded password in Spring Security!
First, please make sure you've already installed JDK on your Centos box. There are tons of tutorials on how to do that :). Now we will download and install Jetty, at the time of this writing, the latest version of Jetty is 7.4.5.v20110725. Every command listing below must be run as user root.
cd /opt
wget http://download.eclipse.org/jetty/stable-7/dist/jetty-distribution-7.4.5.v20110725.tar.gz
tar xzvf jetty-distribution-7.4.5.v20110725.tar.gz
ln -s jetty-distribution-7.4.5.v20110725 jetty
OK, we've just installed Jetty to /opt/jetty. Now we will setup Jetty to run on startup
cd /etc/init.d
ln -s /opt/jetty/bin/jetty.sh jetty
chkconfig --add jetty
chkconfig --level 345 jetty on
It's a good practice to run Jetty as a non-root user, let's create a normal user called jetty
useradd -m jetty
chown -R jetty:jetty /opt/jetty
Now edit the startup script of Jetty located at /opt/jetty/bin/jetty.sh to include the following lines at the beginning (right after a bulk of commented lines describing various variables for Jetty)
JAVA_HOME=/opt/java
JAVA=$JAVA_HOME/bin/java
JAVA_OPTIONS=" -server -Xms256m -Xmx1024m -XX:+DisableExplicitGC "
JETTY_HOME=/opt/jetty
JETTY_USER=jetty
JETTY_PORT=7070
JETTY_HOST=0.0.0.0 # If you don't set this to 0.0.0.0, jetty only listen on localhost
JETTY_LOGS=/opt/jetty/logs/
Now start up it:
service jetty start
Open your browser and navigate to http://:7070/ Hope this help!
I've maintained the ibus-unikey package in Arch User Repository (AUR) for a while and suddenly my package disappeared. While I was wondering what happened to my package, I received an email from a Trusted User (TU) of Arch saying that my package had been moved to the community repo of Arch. Kudo to the ibus-unikey team for such a great piece of software which are so useful for Vietnamese Arch Users!