You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by bu...@apache.org on 2020/05/05 10:42:14 UTC

[cxf] branch master updated: DigestAuthSupplier: fix nc-value and update cnonce generation (#666)

This is an automated email from the ASF dual-hosted git repository.

buhhunyx pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/master by this push:
     new 41c6c00  DigestAuthSupplier: fix nc-value and update cnonce generation (#666)
41c6c00 is described below

commit 41c6c0059f6745098c6f728cd652cbafe83bc986
Author: Alexey Markevich <bu...@gmail.com>
AuthorDate: Tue May 5 13:42:02 2020 +0300

    DigestAuthSupplier: fix nc-value and update cnonce generation (#666)
    
    DigestAuthSupplier: fix nc-value and update cnonce generation
---
 .../org/apache/cxf/transport/http/HTTPConduit.java |   4 +-
 .../transport/http/auth/DigestAuthSupplier.java    |  83 ++++--------
 .../cxf/transport/http/auth/HttpAuthHeader.java    |   3 +-
 .../http/auth/DigestAuthSupplierTest.java          |  14 +-
 systests/transports/pom.xml                        |  45 +++++++
 .../http/auth/DigestAuthSupplierJettyTest.java     | 140 ++++++++++++++++++++
 .../http/auth/DigestAuthSupplierSpringTest.java    | 145 +++++++++++++++++++++
 .../test/resources/digestauth/WEB-INF/beans.xml    |   3 +-
 8 files changed, 362 insertions(+), 75 deletions(-)

diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
index a4d7193..4f44e9e 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
@@ -1934,7 +1934,7 @@ public abstract class HTTPConduit
         // retransmit, it means we have already supplied information
         // which must have been wrong, or we wouldn't be here again.
         // Otherwise, the server may be 401 looping us around the realms.
-        if (authURLs.contains(currentURL.toString() + realm)) {
+        if (!authURLs.add(currentURL.toString() + realm)) {
             String logMessage = "Authorization loop detected on Conduit \""
                 + conduitName
                 + "\" on URL \""
@@ -1948,7 +1948,5 @@ public abstract class HTTPConduit
 
             throw new IOException(logMessage);
         }
-        // Register that we have been here before we go.
-        authURLs.add(currentURL.toString() + realm);
     }
 }
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/DigestAuthSupplier.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/DigestAuthSupplier.java
index 4824fbb..f2e4488 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/DigestAuthSupplier.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/DigestAuthSupplier.java
@@ -19,38 +19,26 @@
 
 package org.apache.cxf.transport.http.auth;
 
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
+import java.nio.ByteBuffer;
 import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.configuration.security.AuthorizationPolicy;
 import org.apache.cxf.message.Message;
 
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
 /**
  *
  */
 public class DigestAuthSupplier implements HttpAuthSupplier {
-    private static final char[] HEXADECIMAL = {
-        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
-    };
 
-    final MessageDigest md5Helper;
     Map<URI, DigestInfo> authInfo = new ConcurrentHashMap<>();
 
-    public DigestAuthSupplier() {
-        MessageDigest md = null;
-        try {
-            md = MessageDigest.getInstance("MD5");
-        } catch (NoSuchAlgorithmException e) {
-            //ignore - set to null
-        }
-        md5Helper = md;
-    }
-
     /**
      * {@inheritDoc}
      * With digest, the nonce could expire and thus a rechallenge will be issued.
@@ -119,13 +107,8 @@ public class DigestAuthSupplier implements HttpAuthSupplier {
         return authURI;
     }
 
-    public String createCnonce() throws UnsupportedEncodingException {
-        String cnonce = Long.toString(System.currentTimeMillis());
-        byte[] bytes = cnonce.getBytes("US-ASCII");
-        synchronized (md5Helper) {
-            bytes = md5Helper.digest(bytes);
-        }
-        return encode(bytes);
+    public String createCnonce() {
+        return Long.toString(System.currentTimeMillis());
     }
 
     class DigestInfo {
@@ -140,31 +123,31 @@ public class DigestAuthSupplier implements HttpAuthSupplier {
 
         synchronized String generateAuth(String uri, String username, String password) {
             try {
-                nc++;
-                String ncstring = String.format("%08d", nc);
-                String cnonce = createCnonce();
-
                 String digAlg = algorithm;
                 if ("MD5-sess".equalsIgnoreCase(digAlg)) {
                     digAlg = "MD5";
                 }
-                MessageDigest digester = MessageDigest.getInstance(digAlg);
-                String a1 = username + ":" + realm + ":" + password;
+                final MessageDigest digester = MessageDigest.getInstance(digAlg);
+                String cnonce = createCnonce();
+                String a1 = username + ':' + realm + ':' + password;
                 if ("MD5-sess".equalsIgnoreCase(algorithm)) {
-                    String tmp2 = encode(digester.digest(a1.getBytes(charset)));
+                    String tmp2 = StringUtils.toHexString(digester.digest(a1.getBytes(charset)));
                     a1 = tmp2 + ':' + nonce + ':' + cnonce;
                 }
-                String hasha1 = encode(digester.digest(a1.getBytes(charset)));
-                String a2 = method + ":" + uri;
-                String hasha2 = encode(digester.digest(a2.getBytes("US-ASCII")));
-                String serverDigestValue = null;
+                String hasha1 = StringUtils.toHexString(digester.digest(a1.getBytes(charset)));
+                String a2 = method + ':' + uri;
+                String hasha2 = StringUtils.toHexString(digester.digest(a2.getBytes(US_ASCII)));
+                final String serverDigestValue;
+                final String ncstring;
                 if (qop == null) {
-                    serverDigestValue = hasha1 + ":" + nonce + ":" + hasha2;
+                    ncstring = null;
+                    serverDigestValue = hasha1 + ':' + nonce + ':' + hasha2;
                 } else {
-                    serverDigestValue = hasha1 + ":" + nonce + ":" + ncstring + ":" + cnonce + ":"
-                        + qop + ":" + hasha2;
+                    ncstring = StringUtils.toHexString(ByteBuffer.allocate(4).putInt(++nc).array());
+                    serverDigestValue = hasha1 + ':' + nonce + ':' + ncstring + ':' + cnonce + ':'
+                        + qop + ':' + hasha2;
                 }
-                String response = encode(digester.digest(serverDigestValue.getBytes("US-ASCII")));
+                String response = StringUtils.toHexString(digester.digest(serverDigestValue.getBytes(US_ASCII)));
                 Map<String, String> outParams = new HashMap<>();
                 if (qop != null) {
                     outParams.put("qop", "auth");
@@ -174,7 +157,9 @@ public class DigestAuthSupplier implements HttpAuthSupplier {
                 outParams.put("nonce", nonce);
                 outParams.put("uri", uri);
                 outParams.put("username", username);
-                outParams.put("nc", ncstring);
+                if (ncstring != null) {
+                    outParams.put("nc", ncstring);
+                }
                 outParams.put("cnonce", cnonce);
                 outParams.put("response", response);
                 outParams.put("algorithm", algorithm);
@@ -187,24 +172,4 @@ public class DigestAuthSupplier implements HttpAuthSupplier {
 
     }
 
-    /**
-     * Encodes the 128 bit (16 bytes) MD5 digest into a 32 characters long
-     * <CODE>String</CODE> according to RFC 2617.
-     *
-     * @param binaryData array containing the digest
-     * @return encoded MD5, or <CODE>null</CODE> if encoding failed
-     */
-    private static String encode(byte[] binaryData) {
-        int n = binaryData.length;
-        char[] buffer = new char[n * 2];
-        for (int i = 0; i < n; i++) {
-            int low = binaryData[i] & 0x0f;
-            int high = (binaryData[i] & 0xf0) >> 4;
-            buffer[i * 2] = HEXADECIMAL[high];
-            buffer[(i * 2) + 1] = HEXADECIMAL[low];
-        }
-
-        return new String(buffer);
-    }
-
 }
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/HttpAuthHeader.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/HttpAuthHeader.java
index 6d3b37d..f7b8469 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/HttpAuthHeader.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/auth/HttpAuthHeader.java
@@ -135,8 +135,7 @@ public final class HttpAuthHeader {
      * @return The realm, or null if it is non-existent.
      */
     public String getRealm() {
-        Map<String, String> map = parseHeader();
-        return map.get("realm");
+        return params.get("realm");
     }
 
     public boolean authTypeIsDigest() {
diff --git a/rt/transports/http/src/test/java/org/apache/cxf/transport/http/auth/DigestAuthSupplierTest.java b/rt/transports/http/src/test/java/org/apache/cxf/transport/http/auth/DigestAuthSupplierTest.java
index 6bee107..e9dc03c 100644
--- a/rt/transports/http/src/test/java/org/apache/cxf/transport/http/auth/DigestAuthSupplierTest.java
+++ b/rt/transports/http/src/test/java/org/apache/cxf/transport/http/auth/DigestAuthSupplierTest.java
@@ -18,7 +18,6 @@
  */
 package org.apache.cxf.transport.http.auth;
 
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
@@ -27,11 +26,10 @@ import org.apache.cxf.configuration.security.AuthorizationPolicy;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.message.MessageImpl;
 
-import org.easymock.EasyMock;
-import org.easymock.IMocksControl;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 public class DigestAuthSupplierTest {
 
@@ -64,35 +62,33 @@ public class DigestAuthSupplierTest {
         DigestAuthSupplier authSupplier = new DigestAuthSupplier() {
 
             @Override
-            public String createCnonce() throws UnsupportedEncodingException {
+            public String createCnonce() {
                 return "27db039b76362f3d55da10652baee38c";
             }
 
         };
-        IMocksControl control = EasyMock.createControl();
         AuthorizationPolicy authorizationPolicy = new AuthorizationPolicy();
         authorizationPolicy.setUserName("testUser");
         authorizationPolicy.setPassword("testPassword");
         URI uri = new URI("http://myserver");
         Message message = new MessageImpl();
-        control.replay();
 
         String authToken = authSupplier
             .getAuthorization(authorizationPolicy, uri, message, fullHeader);
         HttpAuthHeader authHeader = new HttpAuthHeader(authToken);
-        assertEquals("Digest", authHeader.getAuthType());
+        assertTrue(authHeader.authTypeIsDigest());
+
         Map<String, String> params = authHeader.getParams();
         Map<String, String> expectedParams = new HashMap<>();
         expectedParams.put("response", "28e616b6868f60aaf9b19bb5b172f076");
         expectedParams.put("cnonce", "27db039b76362f3d55da10652baee38c");
         expectedParams.put("username", "testUser");
         expectedParams.put("nc", "00000001");
-        expectedParams.put("nonce", "MTI0ODg3OTc5NzE2OTplZGUyYTg0Yzk2NTFkY2YyNjc1Y2JjZjU2MTUzZmQyYw==");
+        expectedParams.put("nonce", origNonce);
         expectedParams.put("realm", "MyCompany realm.");
         expectedParams.put("qop", "auth");
         expectedParams.put("uri", "");
         expectedParams.put("algorithm", "MD5");
         assertEquals(expectedParams, params);
-        control.verify();
     }
 }
diff --git a/systests/transports/pom.xml b/systests/transports/pom.xml
index 44e1f7b..5939a10 100644
--- a/systests/transports/pom.xml
+++ b/systests/transports/pom.xml
@@ -130,6 +130,12 @@
             <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-rt-transports-http-hc</artifactId>
             <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>jcl-over-slf4j</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.apache.cxf</groupId>
@@ -148,6 +154,12 @@
             <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-rt-transports-http-jetty</artifactId>
             <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.servlet</groupId>
+                    <artifactId>javax.servlet-api</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
@@ -246,6 +258,39 @@
             <version>${cxf.spring.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-config</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-web</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <version>${cxf.spring.boot.version}</version>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <version>${cxf.spring.boot.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-rs-client</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestAuthSupplierJettyTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestAuthSupplierJettyTest.java
new file mode 100644
index 0000000..3debbca
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestAuthSupplierJettyTest.java
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.systest.http.auth;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.NotAuthorizedException;
+
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.testutil.common.AbstractClientServerTestBase;
+import org.apache.cxf.testutil.common.AbstractTestServerBase;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transport.http.auth.DigestAuthSupplier;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.security.UserStore;
+import org.eclipse.jetty.security.authentication.DigestAuthenticator;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.util.security.Credential;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+public class DigestAuthSupplierJettyTest extends AbstractClientServerTestBase {
+
+    private static final String USER = "alice";
+    private static final String PWD = "ecila";
+
+    @BeforeClass
+    public static void startServer() throws Exception {
+        launchServer(DigestAuthSupplierJettyServer.class);
+    }
+
+    @Test
+    public void test() {
+        WebClient client = WebClient.create("http://localhost:" + DigestAuthSupplierJettyServer.PORT, (String) null);
+
+        assertThrows(NotAuthorizedException.class, () -> client.get(String.class));
+
+        HTTPConduit conduit = WebClient.getConfig(client).getHttpConduit();
+        conduit.setAuthSupplier(new DigestAuthSupplier());
+        conduit.getAuthorization().setUserName(USER);
+        conduit.getAuthorization().setPassword(PWD);
+
+        assertEquals(TestServlet.RESPONSE, client.get(String.class));
+    }
+
+    public static class DigestAuthSupplierJettyServer extends AbstractTestServerBase {
+        private static final int PORT = allocatePortAsInt(DigestAuthSupplierJettyServer.class);
+
+        private static Server server;
+
+        @Override
+        protected void run() {
+            server = new Server(PORT);
+
+            HashLoginService loginService = new HashLoginService();
+            loginService.setName("My Realm");
+            UserStore userStore = new UserStore();
+            String[] roles = new String[] {"user"};
+            userStore.addUser(USER, Credential.getCredential(PWD), roles);
+            loginService.setUserStore(userStore);
+
+            Constraint constraint = new Constraint();
+            constraint.setName(Constraint.__DIGEST_AUTH);
+            constraint.setRoles(roles);
+            constraint.setAuthenticate(true);
+
+            ConstraintMapping cm = new ConstraintMapping();
+            cm.setConstraint(constraint);
+            cm.setPathSpec("/*");
+
+            ConstraintSecurityHandler csh = new ConstraintSecurityHandler();
+            csh.setAuthenticator(new DigestAuthenticator());
+            csh.addConstraintMapping(cm);
+            csh.setLoginService(loginService);
+
+            ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+            context.setSecurityHandler(csh);
+            context.setContextPath("/");
+            server.setHandler(context);
+            context.addServlet(new ServletHolder(new TestServlet()), "/*");
+
+            try {
+                server.start();
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void tearDown() throws Exception {
+            if (server != null) {
+                server.stop();
+                server.destroy();
+                server = null;
+            }
+        }
+    }
+
+    @SuppressWarnings("serial")
+    static class TestServlet extends HttpServlet {
+
+        static final String RESPONSE = "Hi!";
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+            resp.getWriter().print(RESPONSE);
+        }
+
+    }
+
+}
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestAuthSupplierSpringTest.java b/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestAuthSupplierSpringTest.java
new file mode 100644
index 0000000..f14e0db
--- /dev/null
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http/auth/DigestAuthSupplierSpringTest.java
@@ -0,0 +1,145 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.systest.http.auth;
+
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.core.MediaType;
+
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transport.http.auth.DigestAuthSupplier;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.boot.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint;
+import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(
+    classes = {
+        DigestAuthSupplierSpringTest.SecurityConfig.class,
+        DigestAuthSupplierSpringTest.Controller.class
+    },
+    webEnvironment = WebEnvironment.RANDOM_PORT
+)
+@SpringBootApplication
+public class DigestAuthSupplierSpringTest {
+
+    private static final String USER = "alice";
+    private static final String PWD = "ecila";
+
+    @LocalServerPort
+    private int port;
+
+    @Test
+    public void test() {
+        WebClient client = WebClient.create("http://localhost:" + port, (String) null);
+
+        assertThrows(NotAuthorizedException.class, () -> client.get(String.class));
+
+        HTTPConduit conduit = WebClient.getConfig(client).getHttpConduit();
+        conduit.setAuthSupplier(new DigestAuthSupplier());
+        conduit.getAuthorization().setUserName(USER);
+        conduit.getAuthorization().setPassword(PWD);
+
+        assertEquals(Controller.RESPONSE, client.get(String.class));
+    }
+
+    @RestController
+    static class Controller {
+
+        static final String RESPONSE = "Hi!";
+
+        @GetMapping(produces = MediaType.TEXT_PLAIN)
+        public String get() {
+            return "Hi!";
+        }
+
+    }
+
+    static class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+        @Override
+        protected void configure(HttpSecurity http) throws Exception {
+            DigestAuthenticationEntryPoint authenticationEntryPoint = digestAuthenticationEntryPoint();
+            http
+                .authorizeRequests().anyRequest().authenticated()
+                    .and()
+                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)
+                    .and()
+                .addFilter(digestAuthenticationFilter(authenticationEntryPoint));
+        }
+
+        private DigestAuthenticationFilter digestAuthenticationFilter(
+            DigestAuthenticationEntryPoint authenticationEntryPoint) {
+            DigestAuthenticationFilter digestAuthenticationFilter = new DigestAuthenticationFilter();
+            digestAuthenticationFilter.setUserDetailsService(userDetailsService());
+            digestAuthenticationFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
+            return digestAuthenticationFilter;
+        }
+
+        private static DigestAuthenticationEntryPoint digestAuthenticationEntryPoint() {
+            DigestAuthenticationEntryPoint digestAuthenticationEntryPoint = new DigestAuthenticationEntryPoint();
+            digestAuthenticationEntryPoint.setKey("acegi");
+            digestAuthenticationEntryPoint.setRealmName("Digest Realm");
+            return digestAuthenticationEntryPoint;
+        }
+
+        @Override
+        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+            auth.inMemoryAuthentication()
+                .withUser(USER).password(PWD).roles("");
+        }
+
+        @Bean
+        public static PasswordEncoder passwordEncoder() {
+            return new PasswordEncoder() {
+                @Override
+                public String encode(CharSequence rawPassword) {
+                    return rawPassword.toString();
+                }
+                @Override
+                public boolean matches(CharSequence rawPassword, String encodedPassword) {
+                    return rawPassword.toString().equals(encodedPassword);
+                }
+            };
+        }
+    }
+
+    public static void main(String[] args) {
+        SpringApplication.run(DigestAuthSupplierSpringTest.class, args);
+    }
+
+}
diff --git a/systests/transports/src/test/resources/digestauth/WEB-INF/beans.xml b/systests/transports/src/test/resources/digestauth/WEB-INF/beans.xml
index 9c42d17..2040268 100644
--- a/systests/transports/src/test/resources/digestauth/WEB-INF/beans.xml
+++ b/systests/transports/src/test/resources/digestauth/WEB-INF/beans.xml
@@ -31,7 +31,6 @@
     <http:destination name="{http://apache.org/hello_world}Mortimer.http-destination">
         <!-- Nothing to Configure here for Mortimer -->
     </http:destination>
-    <bean id="greeterImpl" class="org.apache.cxf.systest.http.auth.GreeterImpl">
-  </bean>
+    <bean id="greeterImpl" class="org.apache.cxf.systest.http.auth.GreeterImpl" />
     <jaxws:endpoint address="/greeter" implementor="#greeterImpl" publish="true"/>
 </beans>