SES-24: SpnegoAuthenticationProcessingFilter should call AuthenticationSuccessHandler and AuthenticationFailureHandler

This commit is contained in:
Mike Wiesner
2009-12-21 17:15:44 +00:00
parent ffe0f7e57e
commit ec95d63557
2 changed files with 97 additions and 9 deletions

View File

@@ -32,6 +32,8 @@ import org.springframework.security.core.codec.Base64;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider;
import org.springframework.security.extensions.kerberos.KerberosServiceRequestToken;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
@@ -93,9 +95,15 @@ import org.springframework.web.filter.GenericFilterBean;
*/
public class SpnegoAuthenticationProcessingFilter extends GenericFilterBean {
private AuthenticationManager authenticationManager;
@Override
private AuthenticationManager authenticationManager;
private AuthenticationSuccessHandler successHandler;
private AuthenticationFailureHandler failureHandler;
/* (non-Javadoc)
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
@@ -120,12 +128,19 @@ public class SpnegoAuthenticationProcessingFilter extends GenericFilterBean {
// That shouldn't happen, as it is most likely a wrong configuration on the server side
logger.warn("Negotiate Header was invalid: "+header, e);
SecurityContextHolder.clearContext();
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.flushBuffer();
if (failureHandler != null) {
failureHandler.onAuthenticationFailure(request, response, e);
}
else {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.flushBuffer();
}
return;
}
SecurityContextHolder.getContext()
.setAuthentication(authentication);
if (successHandler != null) {
successHandler.onAuthenticationSuccess(request, response, authentication);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
@@ -140,6 +155,31 @@ public class SpnegoAuthenticationProcessingFilter extends GenericFilterBean {
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* This handler is called after a successful authentication.
* One can add additional authentication behavior by setting this.<br />
* Default is null, which means nothing additional happens
*
* @param successHandler
*/
public void setSuccessHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
}
/**
* This handler is called after a failure authentication.
* In most cases you only get Kerberos/SPNEGO failures with a wrong server
* or network configurations and not during runtime. If the client encounters
* an error, he will just stop the communication with server and therefore
* this handler will not be called in this case.<br />
* Default is null, which means that the Filter returns the HTTP 500 code
*
* @param failureHandler
*/
public void setFailureHandler(AuthenticationFailureHandler failureHandler) {
this.failureHandler = failureHandler;
}
/* (non-Javadoc)
* @see org.springframework.web.filter.GenericFilterBean#afterPropertiesSet()
@@ -150,4 +190,5 @@ public class SpnegoAuthenticationProcessingFilter extends GenericFilterBean {
Assert.notNull(this.authenticationManager, "authenticationManager must be specified");
}
}

View File

@@ -18,7 +18,10 @@ package org.springframework.security.extensions.kerberos.web;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
@@ -30,9 +33,12 @@ import org.junit.Test;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.extensions.kerberos.KerberosServiceRequestToken;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
/**
* Test class for {@link SpnegoAuthenticationProcessingFilter}
@@ -50,6 +56,8 @@ public class SpnegoAuthenticationProcessingFilterTest {
private HttpServletRequest request;
private HttpServletResponse response;
private FilterChain chain;
private AuthenticationSuccessHandler successHandler;
private AuthenticationFailureHandler failureHandler;
// data
private static final byte[] TEST_TOKEN = "TestToken".getBytes();
@@ -58,10 +66,11 @@ public class SpnegoAuthenticationProcessingFilterTest {
AuthorityUtils.createAuthorityList("ROLE_ADMIN"), TEST_TOKEN);
private static final String HEADER = "Authorization";
private static final String TOKEN_PREFIX = "Negotiate ";
private static final BadCredentialsException BCE = new BadCredentialsException("");
@Before
public void before() {
public void before() throws Exception {
// mocking
authenticationManager = mock(AuthenticationManager.class);
filter = new SpnegoAuthenticationProcessingFilter();
@@ -69,10 +78,24 @@ public class SpnegoAuthenticationProcessingFilterTest {
request = mock(HttpServletRequest.class);
response = mock(HttpServletResponse.class);
chain = mock(FilterChain.class);
filter.afterPropertiesSet();
}
@Test
public void testEverythingWorks() throws Exception {
everythingWorks();
}
@Test
public void testEverythingWorksWithHandlers() throws Exception {
createHandler();
everythingWorks();
verify(successHandler).onAuthenticationSuccess(request, response, AUTHENTICATION);
verify(failureHandler, never()).onAuthenticationFailure(any(HttpServletRequest.class),
any(HttpServletResponse.class), any(AuthenticationException.class));
}
private void everythingWorks() throws IOException, ServletException {
// stubbing
when(request.getHeader(HEADER)).thenReturn(TOKEN_PREFIX+TEST_TOKEN_BASE64);
when(authenticationManager.authenticate(new KerberosServiceRequestToken(TEST_TOKEN))).thenReturn(AUTHENTICATION);
@@ -82,6 +105,8 @@ public class SpnegoAuthenticationProcessingFilterTest {
verify(chain).doFilter(request, response);
assertEquals(AUTHENTICATION, SecurityContextHolder.getContext().getAuthentication());
}
@Test
public void testNoHeader() throws Exception {
@@ -95,17 +120,39 @@ public class SpnegoAuthenticationProcessingFilterTest {
@Test
public void testAuthenticationFails() throws Exception {
authenticationFails();
verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
@Test
public void testAuthenticationFailsWithHandlers() throws Exception {
createHandler();
authenticationFails();
verify(failureHandler).onAuthenticationFailure(request, response, BCE);
verify(successHandler, never()).onAuthenticationSuccess(any(HttpServletRequest.class),
any(HttpServletResponse.class), any(Authentication.class));
verify(response, never()).setStatus(anyInt());
}
private void authenticationFails() throws IOException, ServletException {
// stubbing
when(request.getHeader(HEADER)).thenReturn(TOKEN_PREFIX+TEST_TOKEN_BASE64);
when(authenticationManager.authenticate(any(Authentication.class))).thenThrow(new BadCredentialsException(""));
when(authenticationManager.authenticate(any(Authentication.class))).thenThrow(BCE);
// testing
filter.doFilter(request, response, chain);
// chain should stop here and it should send back a 500
// future version should call some error handler
verify(chain, never()).doFilter(any(ServletRequest.class), any(ServletResponse.class));
verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
private void createHandler() {
successHandler = mock(AuthenticationSuccessHandler.class);
failureHandler = mock(AuthenticationFailureHandler.class);
filter.setSuccessHandler(successHandler);
filter.setFailureHandler(failureHandler);
}
@After
public void after() {