Allow forward URL to choose HTTP method in SPNEGO

Closes gh-108
This commit is contained in:
Jan Zyka
2017-04-09 21:00:22 +02:00
committed by Rob Winch
parent b65dfc2877
commit 36509985f6
2 changed files with 69 additions and 4 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2009-2015 the original author or authors.
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.springframework.security.kerberos.web.authentication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.UrlUtils;
@@ -26,6 +27,7 @@ import org.springframework.util.StringUtils;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -57,6 +59,8 @@ public class SpnegoEntryPoint implements AuthenticationEntryPoint {
private final String forwardUrl;
private final HttpMethod forwardMethod;
private final boolean forward;
/**
@@ -71,8 +75,8 @@ public class SpnegoEntryPoint implements AuthenticationEntryPoint {
/**
* Instantiates a new spnego entry point. This constructor enables security
* configuration to use SPNEGO in combination with login form as fallback
* for clients that do not support this kind of authentication.
* configuration to use SPNEGO in combination with a fallback page (login form,
* custom 401 page ...). The forward method will be the same as the original request.
*
* @param forwardUrl
* URL where the login page can be found. Should be
@@ -80,13 +84,34 @@ public class SpnegoEntryPoint implements AuthenticationEntryPoint {
* {@code /}) and can't be absolute URL.
*/
public SpnegoEntryPoint(String forwardUrl) {
this(forwardUrl, null);
}
/**
* Instantiates a new spnego entry point. This constructor enables security
* configuration to use SPNEGO in combination a fallback page (login form,
* custom 401 page ...). The forward URL will be accessed via provided HTTP
* method.
*
* @param forwardUrl
* URL where the login page can be found. Should be
* relative to the web-app context path (include a leading
* {@code /}) and can't be absolute URL.
*
* @param forwardMethod
* HTTP method to use when accessing the forward URL
*/
public SpnegoEntryPoint(String forwardUrl, HttpMethod forwardMethod) {
if (StringUtils.hasText(forwardUrl)) {
Assert.isTrue(UrlUtils.isValidRedirectUrl(forwardUrl), "Forward url specified must be a valid forward URL");
Assert.isTrue(!UrlUtils.isAbsoluteUrl(forwardUrl), "Forward url specified must not be absolute");
this.forwardUrl = forwardUrl;
this.forwardMethod = forwardMethod;
this.forward = true;
} else {
this.forwardUrl = null;
this.forwardMethod = null;
this.forward = false;
}
}
@@ -102,7 +127,13 @@ public class SpnegoEntryPoint implements AuthenticationEntryPoint {
if (forward) {
RequestDispatcher dispatcher = request.getRequestDispatcher(forwardUrl);
dispatcher.forward(request, response);
HttpServletRequest fwdRequest = forwardMethod != null ? new HttpServletRequestWrapper(request) {
@Override
public String getMethod() {
return forwardMethod.name();
}
} : request;
dispatcher.forward(fwdRequest, response);
} else {
response.flushBuffer();
}

View File

@@ -17,10 +17,14 @@ package org.springframework.security.kerberos.web;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.http.HttpMethod;
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,6 +32,7 @@ import static org.mockito.Mockito.when;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Test class for {@link SpnegoEntryPoint}
@@ -79,6 +84,35 @@ public class SpnegoEntryPointTest {
verify(requestDispatcher).forward(request, response);
}
@Test
public void testForwardUsesDefaultHttpMethod() throws Exception {
ArgumentCaptor<HttpServletRequest> servletRequestCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);
String forwardUrl = "/login";
SpnegoEntryPoint entryPoint = new SpnegoEntryPoint(forwardUrl);
HttpServletResponse response = mock(HttpServletResponse.class);
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getMethod()).thenReturn(RequestMethod.POST.name());
RequestDispatcher requestDispatcher = mock(RequestDispatcher.class);
when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher);
entryPoint.commence(request, response, null);
verify(requestDispatcher).forward(servletRequestCaptor.capture(), eq(response));
assertThat(servletRequestCaptor.getValue().getMethod()).isEqualTo(HttpMethod.POST.name());
}
@Test
public void testForwardUsesCustomHttpMethod() throws Exception {
ArgumentCaptor<HttpServletRequest> servletRequestCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);
String forwardUrl = "/login";
SpnegoEntryPoint entryPoint = new SpnegoEntryPoint(forwardUrl, HttpMethod.DELETE);
HttpServletResponse response = mock(HttpServletResponse.class);
HttpServletRequest request = mock(HttpServletRequest.class);
RequestDispatcher requestDispatcher = mock(RequestDispatcher.class);
when(request.getRequestDispatcher(anyString())).thenReturn(requestDispatcher);
entryPoint.commence(request, response, null);
verify(requestDispatcher).forward(servletRequestCaptor.capture(), eq(response));
assertThat(servletRequestCaptor.getValue().getMethod()).isEqualTo(HttpMethod.DELETE.name());
}
@Test
public void testEntryPointForwardAbsolute() throws Exception {
assertThatThrownBy(() -> {