KEYCLOAK-4148 Prevent unnecessary deserialization when supported

... and gain another ~ 5-10 %
This commit is contained in:
Hynek Mlnarik 2017-01-04 15:47:16 +01:00
parent 862502f3ed
commit ad9210a7a7
5 changed files with 86 additions and 29 deletions

View file

@ -33,6 +33,9 @@ import javax.xml.stream.events.Characters;
import javax.xml.stream.events.XMLEvent; import javax.xml.stream.events.XMLEvent;
import javax.xml.stream.util.EventReaderDelegate; import javax.xml.stream.util.EventReaderDelegate;
import java.io.InputStream; import java.io.InputStream;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Node;
/** /**
* Base class for parsers * Base class for parsers
@ -49,28 +52,28 @@ public abstract class AbstractParser implements ParserNamespaceSupport {
protected XMLInputFactory initialValue() { protected XMLInputFactory initialValue() {
return getXMLInputFactory(); return getXMLInputFactory();
} }
};
/** /**
* Get the JAXP {@link XMLInputFactory} * Get the JAXP {@link XMLInputFactory}
* *
* @return * @return
*/ */
private static XMLInputFactory getXMLInputFactory() { private XMLInputFactory getXMLInputFactory() {
boolean tccl_jaxp = SystemPropertiesUtil.getSystemProperty(GeneralConstants.TCCL_JAXP, "false") boolean tccl_jaxp = SystemPropertiesUtil.getSystemProperty(GeneralConstants.TCCL_JAXP, "false")
.equalsIgnoreCase("true"); .equalsIgnoreCase("true");
ClassLoader prevTCCL = SecurityActions.getTCCL(); ClassLoader prevTCCL = SecurityActions.getTCCL();
try { try {
if (tccl_jaxp) { if (tccl_jaxp) {
SecurityActions.setTCCL(AbstractParser.class.getClassLoader()); SecurityActions.setTCCL(AbstractParser.class.getClassLoader());
} }
return XMLInputFactory.newInstance(); return XMLInputFactory.newInstance();
} finally { } finally {
if (tccl_jaxp) { if (tccl_jaxp) {
SecurityActions.setTCCL(prevTCCL); SecurityActions.setTCCL(prevTCCL);
}
} }
} }
} };
/** /**
* Parse an InputStream for payload * Parse an InputStream for payload
@ -87,6 +90,15 @@ public abstract class AbstractParser implements ParserNamespaceSupport {
return parse(xmlEventReader); return parse(xmlEventReader);
} }
public Object parse(Source source) throws ParsingException {
XMLEventReader xmlEventReader = createEventReader(source);
return parse(xmlEventReader);
}
public Object parse(Node node) throws ParsingException {
return parse(new DOMSource(node));
}
public XMLEventReader createEventReader(InputStream configStream) throws ParsingException { public XMLEventReader createEventReader(InputStream configStream) throws ParsingException {
if (configStream == null) if (configStream == null)
throw logger.nullArgumentError("InputStream"); throw logger.nullArgumentError("InputStream");
@ -102,6 +114,21 @@ public abstract class AbstractParser implements ParserNamespaceSupport {
return xmlEventReader; return xmlEventReader;
} }
public XMLEventReader createEventReader(Source source) throws ParsingException {
if (source == null)
throw logger.nullArgumentError("Source");
XMLEventReader xmlEventReader = StaxParserUtil.getXMLEventReader(source);
try {
xmlEventReader = filterWhitespaces(xmlEventReader);
} catch (XMLStreamException e) {
throw logger.parserException(e);
}
return xmlEventReader;
}
protected XMLEventReader filterWhitespaces(XMLEventReader xmlEventReader) throws XMLStreamException { protected XMLEventReader filterWhitespaces(XMLEventReader xmlEventReader) throws XMLStreamException {
XMLInputFactory xmlInputFactory = XML_INPUT_FACTORY.get(); XMLInputFactory xmlInputFactory = XML_INPUT_FACTORY.get();

View file

@ -24,6 +24,8 @@ import org.keycloak.saml.common.constants.JBossSAMLConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.exceptions.ProcessingException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
@ -47,6 +49,7 @@ import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory; import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator; import javax.xml.validation.Validator;
import java.io.InputStream; import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* Utility for the stax based parser * Utility for the stax based parser
@ -250,6 +253,34 @@ public class StaxParserUtil {
return xmlEventReader; return xmlEventReader;
} }
private static AtomicBoolean XML_EVENT_READER_ON_SOURCE_SUPPORTED = new AtomicBoolean(true);
/**
* Get the XML event reader
*
* @param source
*
* @return
*/
public static XMLEventReader getXMLEventReader(Source source) {
XMLInputFactory xmlInputFactory = XML_INPUT_FACTORY.get();
try {
if (XML_EVENT_READER_ON_SOURCE_SUPPORTED.get()) {
try {
// The following method is optional per specification
return xmlInputFactory.createXMLEventReader(source);
} catch (UnsupportedOperationException ex) {
XML_EVENT_READER_ON_SOURCE_SUPPORTED.set(false);
return getXMLEventReader(source);
}
} else {
return getXMLEventReader(DocumentUtil.getSourceAsStream(source));
}
} catch (ConfigurationException | ProcessingException | XMLStreamException ex) {
throw new RuntimeException(ex);
}
}
/** /**
* Given a {@code Location}, return a formatted string [lineNum,colNum] * Given a {@code Location}, return a formatted string [lineNum,colNum]
* *

View file

@ -165,7 +165,7 @@ public class SAML2Request {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
SAML2Object requestType = (SAML2Object) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); SAML2Object requestType = (SAML2Object) samlParser.parse(samlDocument);
samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument); samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument);
return requestType; return requestType;
@ -192,7 +192,7 @@ public class SAML2Request {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
RequestAbstractType requestType = (RequestAbstractType) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); RequestAbstractType requestType = (RequestAbstractType) samlParser.parse(samlDocument);
samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument); samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument);
return requestType; return requestType;
@ -220,7 +220,7 @@ public class SAML2Request {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
AuthnRequestType requestType = (AuthnRequestType) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); AuthnRequestType requestType = (AuthnRequestType) samlParser.parse(samlDocument);
samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument); samlDocumentHolder = new SAMLDocumentHolder(requestType, samlDocument);
return requestType; return requestType;
} }

View file

@ -376,7 +376,7 @@ public class SAML2Response {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
return (EncryptedAssertionType) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); return (EncryptedAssertionType) samlParser.parse(samlDocument);
} }
@ -398,7 +398,7 @@ public class SAML2Response {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlDocument); JAXPValidationUtil.checkSchemaValidation(samlDocument);
return (AssertionType) samlParser.parse(DocumentUtil.getNodeAsStream(samlDocument)); return (AssertionType) samlParser.parse(samlDocument);
} }
/** /**
@ -429,7 +429,7 @@ public class SAML2Response {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlResponseDocument); JAXPValidationUtil.checkSchemaValidation(samlResponseDocument);
ResponseType responseType = (ResponseType) samlParser.parse(DocumentUtil.getNodeAsStream(samlResponseDocument)); ResponseType responseType = (ResponseType) samlParser.parse(samlResponseDocument);
samlDocumentHolder = new SAMLDocumentHolder(responseType, samlResponseDocument); samlDocumentHolder = new SAMLDocumentHolder(responseType, samlResponseDocument);
return responseType; return responseType;
@ -460,8 +460,7 @@ public class SAML2Response {
SAMLParser samlParser = new SAMLParser(); SAMLParser samlParser = new SAMLParser();
JAXPValidationUtil.checkSchemaValidation(samlResponseDocument); JAXPValidationUtil.checkSchemaValidation(samlResponseDocument);
InputStream responseStream = DocumentUtil.getNodeAsStream(samlResponseDocument); SAML2Object responseType = (SAML2Object) samlParser.parse(samlResponseDocument);
SAML2Object responseType = (SAML2Object) samlParser.parse(responseStream);
samlDocumentHolder = new SAMLDocumentHolder(responseType, samlResponseDocument); samlDocumentHolder = new SAMLDocumentHolder(responseType, samlResponseDocument);
return responseType; return responseType;

View file

@ -146,7 +146,7 @@ public class SamlEcpProfileTest {
assertNotNull(samlResponse); assertNotNull(samlResponse);
ResponseType responseType = (ResponseType) new SAMLParser().parse(DocumentUtil.getNodeAsStream(samlResponse)); ResponseType responseType = (ResponseType) new SAMLParser().parse(samlResponse);
StatusCodeType statusCode = responseType.getStatus().getStatusCode(); StatusCodeType statusCode = responseType.getStatus().getStatusCode();
assertEquals(statusCode.getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get()); assertEquals(statusCode.getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());
@ -229,7 +229,7 @@ public class SamlEcpProfileTest {
assertNotNull(samlResponse); assertNotNull(samlResponse);
StatusResponseType responseType = (StatusResponseType) new SAMLParser().parse(DocumentUtil.getNodeAsStream(samlResponse)); StatusResponseType responseType = (StatusResponseType) new SAMLParser().parse(samlResponse);
StatusCodeType statusCode = responseType.getStatus().getStatusCode(); StatusCodeType statusCode = responseType.getStatus().getStatusCode();
assertNotEquals(statusCode.getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get()); assertNotEquals(statusCode.getStatusCode().getValue().toString(), JBossSAMLURIConstants.STATUS_SUCCESS.get());