diff --git a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java index e5fc91940c..a2c442405a 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java @@ -17,6 +17,7 @@ package org.springframework.security.web.authentication.www; import java.io.IOException; +import java.nio.charset.Charset; import javax.servlet.FilterChain; import javax.servlet.ServletException; @@ -276,6 +277,7 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter { public void setCredentialsCharset(String credentialsCharset) { Assert.hasText(credentialsCharset, "credentialsCharset cannot be null or empty"); this.credentialsCharset = credentialsCharset; + this.authenticationConverter.setCredentialsCharset(Charset.forName(credentialsCharset)); } protected String getCredentialsCharset(HttpServletRequest httpRequest) { diff --git a/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java index 3b604a3cd1..c03b132807 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java @@ -16,13 +16,14 @@ package org.springframework.security.web.authentication.www; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import static org.mockito.AdditionalMatchers.not; import static org.mockito.Mockito.*; import javax.servlet.FilterChain; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import org.junit.After; @@ -40,6 +41,8 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.web.util.WebUtils; +import java.nio.charset.StandardCharsets; + /** * Tests {@link BasicAuthenticationFilter}. * @@ -320,4 +323,105 @@ public class BasicAuthenticationFilterTests { assertThat(response.getStatus()).isEqualTo(200); } + + @Test + public void doFilterWhenTokenAndFilterCharsetMatchDefaultThenAuthenticated() throws Exception { + SecurityContextHolder.clearContext(); + + UsernamePasswordAuthenticationToken rodRequest = new UsernamePasswordAuthenticationToken("rod", "äöü"); + rodRequest.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())); + Authentication rod = new UsernamePasswordAuthenticationToken("rod", "äöü", AuthorityUtils.createAuthorityList("ROLE_1")); + + manager = mock(AuthenticationManager.class); + when(manager.authenticate(rodRequest)).thenReturn(rod); + when(manager.authenticate(not(eq(rodRequest)))).thenThrow(new BadCredentialsException("")); + + filter = new BasicAuthenticationFilter(manager, new BasicAuthenticationEntryPoint()); + + String token = "rod:äöü"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes(StandardCharsets.UTF_8)))); + request.setServletPath("/some_file.html"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + + // Test + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); + FilterChain chain = mock(FilterChain.class); + + filter.doFilter(request, response, chain); + + assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK); + verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + assertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo("rod"); + assertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo("äöü"); + } + + @Test + public void doFilterWhenTokenAndFilterCharsetMatchNonDefaultThenAuthenticated() throws Exception { + SecurityContextHolder.clearContext(); + + UsernamePasswordAuthenticationToken rodRequest = new UsernamePasswordAuthenticationToken("rod", "äöü"); + rodRequest.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())); + Authentication rod = new UsernamePasswordAuthenticationToken("rod", "äöü", AuthorityUtils.createAuthorityList("ROLE_1")); + + manager = mock(AuthenticationManager.class); + when(manager.authenticate(rodRequest)).thenReturn(rod); + when(manager.authenticate(not(eq(rodRequest)))).thenThrow(new BadCredentialsException("")); + + filter = new BasicAuthenticationFilter(manager, new BasicAuthenticationEntryPoint()); + filter.setCredentialsCharset("ISO-8859-1"); + + String token = "rod:äöü"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes(StandardCharsets.ISO_8859_1)))); + request.setServletPath("/some_file.html"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + + // Test + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); + FilterChain chain = mock(FilterChain.class); + + filter.doFilter(request, response, chain); + + assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK); + verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + assertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo("rod"); + assertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo("äöü"); + } + + @Test + public void doFilterWhenTokenAndFilterCharsetDoNotMatchThenUnauthorized() throws Exception { + SecurityContextHolder.clearContext(); + + UsernamePasswordAuthenticationToken rodRequest = new UsernamePasswordAuthenticationToken("rod", "äöü"); + rodRequest.setDetails(new WebAuthenticationDetails(new MockHttpServletRequest())); + Authentication rod = new UsernamePasswordAuthenticationToken("rod", "äöü", AuthorityUtils.createAuthorityList("ROLE_1")); + + manager = mock(AuthenticationManager.class); + when(manager.authenticate(rodRequest)).thenReturn(rod); + when(manager.authenticate(not(eq(rodRequest)))).thenThrow(new BadCredentialsException("")); + + filter = new BasicAuthenticationFilter(manager, new BasicAuthenticationEntryPoint()); + filter.setCredentialsCharset("ISO-8859-1"); + + String token = "rod:äöü"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("Authorization", "Basic " + new String(Base64.encodeBase64(token.getBytes(StandardCharsets.UTF_8)))); + request.setServletPath("/some_file.html"); + + MockHttpServletResponse response = new MockHttpServletResponse(); + + // Test + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); + FilterChain chain = mock(FilterChain.class); + + filter.doFilter(request, response, chain); + + assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED); + verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class)); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); + } + }