405435 Implement servlet3.1 section 13.6.3 for 303 redirects for Form auth

This commit is contained in:
Jan Bartel 2013-05-02 17:34:06 +10:00
parent fb43fd4c9e
commit 348cbc2173
6 changed files with 125 additions and 27 deletions

View File

@ -52,9 +52,25 @@ public interface Authenticator
* @return The name of the authentication method
*/
String getAuthMethod();
/* ------------------------------------------------------------ */
/**
* Called prior to validateRequest. The authenticator can
* manipulate the request to update it with information that
* can be inspected prior to validateRequest being called.
* The primary purpose of this method is to satisfy the Servlet
* Spec 3.1 section 13.6.3 on handling Form authentication
* where the http method of the original request causing authentication
* is not the same as the http method resulting from the redirect
* after authentication.
* @param request
*/
void prepareRequest(ServletRequest request);
/* ------------------------------------------------------------ */
/** Validate a response
/** Validate a request
* @param request The request
* @param response The response
* @param mandatory True if authentication is mandatory.

View File

@ -462,6 +462,10 @@ public abstract class SecurityHandler extends HandlerWrapper implements Authenti
if (checkSecurity(baseRequest))
{
//See Servlet Spec 3.1 sec 13.6.3
if (authenticator != null)
authenticator.prepareRequest(baseRequest);
RoleInfo roleInfo = prepareConstraintInfo(pathInContext, baseRequest);
// Check data constraints

View File

@ -36,6 +36,7 @@ import javax.servlet.http.HttpSession;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.security.ServerAuthException;
import org.eclipse.jetty.security.UserAuthentication;
@ -43,6 +44,7 @@ import org.eclipse.jetty.server.Authentication;
import org.eclipse.jetty.server.Authentication.User;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil;
@ -75,6 +77,7 @@ public class FormAuthenticator extends LoginAuthenticator
public final static String __FORM_DISPATCH="org.eclipse.jetty.security.dispatch";
public final static String __J_URI = "org.eclipse.jetty.security.form_URI";
public final static String __J_POST = "org.eclipse.jetty.security.form_POST";
public final static String __J_METHOD = "org.eclipse.jetty.security.form_METHOD";
public final static String __J_SECURITY_CHECK = "/j_security_check";
public final static String __J_USERNAME = "j_username";
public final static String __J_PASSWORD = "j_password";
@ -198,6 +201,45 @@ public class FormAuthenticator extends LoginAuthenticator
}
return user;
}
/* ------------------------------------------------------------ */
@Override
public void prepareRequest(ServletRequest request)
{
//if this is a request resulting from a redirect after auth is complete
//(ie its from a redirect to the original request uri) then due to
//browser handling of 302 redirects, the method may not be the same as
//that of the original request. Replace the method and original post
//params (if it was a post).
//
//See Servlet Spec 3.1 sec 13.6.3
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpSession session = httpRequest.getSession(false);
if (session == null || session.getAttribute(SessionAuthentication.__J_AUTHENTICATED) == null)
return; //not authenticated yet
String juri = (String)session.getAttribute(__J_URI);
if (juri == null || juri.length() == 0)
return; //no original uri saved
String method = (String)session.getAttribute(__J_METHOD);
if (method == null || method.length() == 0)
return; //didn't save original request method
StringBuffer buf = httpRequest.getRequestURL();
if (httpRequest.getQueryString() != null)
buf.append("?").append(httpRequest.getQueryString());
if (!juri.equals(buf.toString()))
return; //this request is not for the same url as the original
//restore the original request's method on this request
if (LOG.isDebugEnabled()) LOG.debug("Restoring original method {} for {} with method {}", method, juri,httpRequest.getMethod());
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
HttpMethod m = HttpMethod.fromString(method);
base_request.setMethod(m,m.asString());
}
/* ------------------------------------------------------------ */
@Override
@ -249,7 +291,10 @@ public class FormAuthenticator extends LoginAuthenticator
LOG.debug("authenticated {}->{}",form_auth,nuri);
response.setContentLength(0);
response.sendRedirect(response.encodeRedirectURL(nuri));
Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
base_response.sendRedirect(redirectCode, response.encodeRedirectURL(nuri));
return form_auth;
}
@ -273,7 +318,10 @@ public class FormAuthenticator extends LoginAuthenticator
else
{
LOG.debug("auth failed {}->{}",username,_formErrorPage);
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formErrorPage)));
Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
base_response.sendRedirect(redirectCode, response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formErrorPage)));
}
return Authentication.SEND_FAILURE;
@ -298,28 +346,26 @@ public class FormAuthenticator extends LoginAuthenticator
String j_uri=(String)session.getAttribute(__J_URI);
if (j_uri!=null)
{
//check if the request is for the same url as the original and restore
//params if it was a post
LOG.debug("auth retry {}->{}",authentication,j_uri);
MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(__J_POST);
if (j_post!=null)
StringBuffer buf = request.getRequestURL();
if (request.getQueryString() != null)
buf.append("?").append(request.getQueryString());
if (j_uri.equals(buf.toString()))
{
LOG.debug("auth rePOST {}->{}",authentication,j_uri);
StringBuffer buf = request.getRequestURL();
if (request.getQueryString() != null)
buf.append("?").append(request.getQueryString());
if (j_uri.equals(buf.toString()))
MultiMap<String> j_post = (MultiMap<String>)session.getAttribute(__J_POST);
if (j_post!=null)
{
// This is a retry of an original POST request
// so restore method and parameters
session.removeAttribute(__J_POST);
LOG.debug("auth rePOST {}->{}",authentication,j_uri);
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
base_request.setMethod(HttpMethod.POST,HttpMethod.POST.asString());
base_request.setParameters(j_post);
}
}
else
session.removeAttribute(__J_URI);
session.removeAttribute(__J_METHOD);
session.removeAttribute(__J_POST);
}
}
}
LOG.debug("auth {}",authentication);
@ -344,6 +390,7 @@ public class FormAuthenticator extends LoginAuthenticator
if (request.getQueryString() != null)
buf.append("?").append(request.getQueryString());
session.setAttribute(__J_URI, buf.toString());
session.setAttribute(__J_METHOD, request.getMethod());
if (MimeTypes.Type.FORM_ENCODED.is(req.getContentType()) && HttpMethod.POST.is(request.getMethod()))
{
@ -366,7 +413,10 @@ public class FormAuthenticator extends LoginAuthenticator
else
{
LOG.debug("challenge {}->{}",session.getId(),_formLoginPage);
response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formLoginPage)));
Response base_response = HttpChannel.getCurrentHttpChannel().getResponse();
Request base_request = HttpChannel.getCurrentHttpChannel().getRequest();
int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER);
base_response.sendRedirect(redirectCode, response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(),_formLoginPage)));
}
return Authentication.SEND_CONTINUE;
}

View File

@ -40,11 +40,20 @@ public abstract class LoginAuthenticator implements Authenticator
protected LoginService _loginService;
protected IdentityService _identityService;
private boolean _renewSession;
/* ------------------------------------------------------------ */
protected LoginAuthenticator()
{
}
/* ------------------------------------------------------------ */
@Override
public void prepareRequest(ServletRequest request)
{
//empty implementation as the default
}
/* ------------------------------------------------------------ */
public UserIdentity login(String username, Object password, ServletRequest request)
@ -58,7 +67,7 @@ public abstract class LoginAuthenticator implements Authenticator
return null;
}
/* ------------------------------------------------------------ */
@Override
public void setConfiguration(AuthConfiguration configuration)
{
@ -70,12 +79,16 @@ public abstract class LoginAuthenticator implements Authenticator
throw new IllegalStateException("No IdentityService for "+this+" in "+configuration);
_renewSession=configuration.isSessionRenewedOnAuthentication();
}
/* ------------------------------------------------------------ */
public LoginService getLoginService()
{
return _loginService;
}
/* ------------------------------------------------------------ */
/** Change the session id.
* The session is changed to a new instance with a new ID if and only if:<ul>
* <li>A session exists.

View File

@ -686,6 +686,7 @@ public class ConstraintTest
"Content-Length: 31\r\n" +
"\r\n" +
"j_username=user&j_password=wrong\r\n");
assertThat(response,containsString("Location"));
response = _connector.getResponses("POST /ctx/j_security_check HTTP/1.0\r\n" +

View File

@ -428,10 +428,18 @@ public class Response implements HttpServletResponse
_channel.commitResponse(HttpGenerator.PROGRESS_102_INFO, null, true);
}
}
@Override
public void sendRedirect(String location) throws IOException
/**
* Sends a response with one of the 300 series redirection codes.
* @param code
* @param location
* @throws IOException
*/
public void sendRedirect(int code, String location) throws IOException
{
if ((code < HttpServletResponse.SC_MULTIPLE_CHOICES) || (code >= HttpServletResponse.SC_BAD_REQUEST))
throw new IllegalArgumentException("Not a 3xx redirect code");
if (isIncluding())
return;
@ -497,10 +505,16 @@ public class Response implements HttpServletResponse
resetBuffer();
setHeader(HttpHeader.LOCATION, location);
setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
setStatus(code);
complete();
}
@Override
public void sendRedirect(String location) throws IOException
{
sendRedirect(HttpServletResponse.SC_MOVED_TEMPORARILY, location);
}
@Override
public void setDateHeader(String name, long date)
{