how to set maxHttpHeaderSize in spring-boot 3.x - java

As stated in the Spring Boot 3 Migration Guide the server.max-http-header-size property has been deprecated. You can use the server.max-http-request-header-size property to set the max http request header size only.
I get the following Exception:
org.apache.coyote.http11.HeadersTooLargeException: An attempt was made to write
more data to the response headers than there was room available in the buffer.
Increase maxHttpHeaderSize on the connector
or write less data into the response headers.
I need to increase the max http response header size in an embedded Tomcat 10. Tomcat 10 supports both the maxHttpHeaderSize and the maxHttpResponseHeaderSize attributes.
How I can set these in spring boot 3.x with a WebServerFactoryCustomizer?

You can create a WebServerFactoryCustomer as shown in the Spring Boot documentation.
For your use case, that would look something like this:
#Component
public class TomcatCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory server) {
server.addConnectorCustomizers((connector) -> {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11Protocol<?> protocol) {
protocol.setMaxHttpResponseHeaderSize(x);
}
});
}
}
Spring Boot 3.1.0 will introduce a property server.tomcat.maxHttpResponseHeaderSize to make this easier to configure.

Related

Set default response content type in Spring Boot REST API

Response content type on REST API endpoints (controller classes or methods) in Spring Boot can be set using the #Produces annotation. Is there a way to set this application wide as a default for every endpoint in the REST API? For example, instead of writing #Produces("application/json") on every controller class or endpoint, can this be set on the entry application class? Or is there any other way to configure a default used until explicitely overwritten?
If you want to set the default Accept header, not of default "Content-Type" header, so this solution will only impact responses, not requests.
As of Spring Boot 2.x, you need to create a class that extends the WebMvcConfigurer interface, e.g.:
#Configuration
class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void configureContentNegotiation( ContentNegotiationConfigurer configurer){
configurer.defaultContentType( MediaType.APPLICATION_JSON );
}
}
Let me know the result. Good luck.
Another option is to specify this default-media-type property in your application.properties:
spring.data.rest.default-media-type=application/json
Interestingly, it's also shown here as defaultMediaType. I believe you can use either the above or:
spring.data.rest.defaultMediaType=application/json
I'm not sure actually why there appear to be 2 ways to specify this same property.

Logging a request header before Spring Security filter chain

I want to log the contents of a given incoming request header as early as possible.
I know about approaches like CommonsRequestLoggingFilter or a logging HandlerInterceptor, however these seem to only log after Spring has executed a lot of other code, such as the Spring Security filter chain.
I want to log before Spring has done any of that, as early as possible based on a single requirement: the log message needs to be able to extract a header from the HTTP request.
Is there a way to do this?
I have found a way to do this using the embedded Tomcat. Since this receives the request before Spring does, you can capture the entire dispatched request from here.
public class CustomLoggerValve extends ValveBase {
private static final Logger logger = LoggerFactory.getLogger(CustomLoggerValve.class);
#Override
public void invoke(Request request, Response response) throws IOException, ServletException {
try {
MDC.put("requestId", UUID.randomUUID().toString());
logger.info("Received request");
getNext().invoke(request, response);
} finally {
MDC.remove("requestId");
}
}
}
Since I'm using Spring without Spring Boot, I can just add this to my Tomcat directly:
Tomcat tomcat = // ... your tomcat setup
tomcat.getService().getContainer().getPipeline().addValve(new CustomLoggerValve());
I haven't tried, but it looks like you could add this quite easily in Spring Boot too.
Presumably a similar approach would work with embedded Jetty/other JVM web servers.

Use existing http server in spring boot as camel endpoint

I have a spring boot application that uses the spring boot starter web. This creates a running Tomcat instance and sets up the http server running on a port. Within my camel route, I want to use this http server as the component for http requests, but I can't figure out how to utilize it. I see many many examples of configuring a jetty instance and consuming from it, but then wouldn't I in effect have two http servers running? I only want to have one. I assume the http server is already autowired up since I can consume from it with other spring code (such as a RestController) and I can see it started in my spring boot logs as well.
#Component
public class ExampleRoute extends RouteBuilder
{
#Override
public void configure() throws Exception
{
//#formatter:off
from( <want to take in an http request here> )
.log( LoggingLevel.INFO, log, "Hello World!" );
//#formatter:on
}
}
There is an example here: https://github.com/camelinaction/camelinaction2/tree/master/chapter7/springboot-camel
You can to register a ServletRegistrationBean that setup the Camel Servlet with Spring Boot.
#Bean
ServletRegistrationBean camelServlet() {
// use a #Bean to register the Camel servlet which we need to do
// because we want to use the camel-servlet component for the Camel REST service
ServletRegistrationBean mapping = new ServletRegistrationBean();
mapping.setName("CamelServlet");
mapping.setLoadOnStartup(1);
// CamelHttpTransportServlet is the name of the Camel servlet to use
mapping.setServlet(new CamelHttpTransportServlet());
mapping.addUrlMappings("/camel/*");
return mapping;
}
However for Camel 2.19 we plan on make this simpler and OOTB: https://issues.apache.org/jira/browse/CAMEL-10416
And then you can do
from("servlet:foo")
.to("bean:foo");
Where the HTTP url to call that Camel route will be http:localhost:8080/camel/foo

How to get applicationContextPath in dropwizard 1.0.0

We are using server configuration in yml file which looks like as below
server:
type: simple
connector:
type: http
port: 8061
applicationContextPath: /administration
adminContextPath: /admin
#disable the registration of default Jersey ExceptionMappers
registerDefaultExceptionMappers: false
I want to get "applicationContextPath" when I start my dropwizard service.
I am trying to get it using
environment.getApplicationContext().getContextPath();
but I am getting "/" i.e. default value. Is there anyway to get this.
In order get applicationContextPath we need to get ServerFactory from Configuration and parse it to SimpleServerFactory as below:
((SimpleServerFactory) getConfiguration().getServerFactory()).getApplicationContextPath()
This works for me:
#Override
public void run(CustomAppConfiguration customAppConfiguration , Environment environment) throws Exception {
DefaultServerFactory factory = (DefaultServerFactory) customAppConfiguration .getServerFactory();
System.out.println("CONTEXT PATH: "+factory.getApplicationContextPath());
...
}
If it's in your config file and you want to just read the value in as it exist in your config.yml, then I'd suggest making it part of your Configuration class. Values in your config can always be accessed this way regardless of whether dropwizard uses and treats those key/values in special manner internally.
The following worked for me in dropwizard 1.0.0:
MyApp.java:
public class MyApp extends Application<MyConfig> {
//...
#Override
public void run(MyConfig configuration, Environment environment) throws Exception {
System.out.println(configuration.contextPath);
//...
MyConfig.java
public class MyConfig extends Configuration {
//...
#JsonProperty("applicationContextPath")
public String contextPath;
//...
If I understood your question correctly what you can do in Dropwizard version 1.3.8 if you are using simple server (without https) you can get applicationContextPath in following way:
server:
type: simple
rootPath: /*
applicationContextPath: /administration
adminContextPath: /admin
connector:
type: http
port: 8080
More info about rootPath can be found in Dropwizard Configuration Reference. So if you want to access:
Application REST endpoint /books (which is value of GET,
POST or similar annotation in one of your Resource class methods) you can
type URL like this http://localhost:8080/administration/books
Metrics (only accessible via admin context path) of your Dropwizard application then you create URL like this: http://localhost:8080/admin/metrics
Hope that helps. Cheers!

Sample SAML Java Config. Project, Broken From the Start

I pulled in the example Java configuration project for Spring's SAML extension. No commits seem to have been made to the project for about six months as of my writing this question. I have not done anything to this project except for run maven package against it.
I then run the application in Spring Tool Suite as a Spring Boot Application and the application runs; however, the application does not run without error and the application endpoint is inaccessible (resulting in am error message): "ERROR: Something went wrong in the authentication process".
I haven't registered any certificates, etc (and may very well need to). There are no instructions provided with the GitHub project for starting or working with the application. I have intentionally not posted the guts of the project as I have left it unmodified.
INFORMATION ON THE ERROR
From Chrome Dev. Tools, I can see a 500 Internal Server Error returned from the request to the localhost:8080 application. So, the issue is definitely with the sample application (or something that I have not done).
The following error is logged to the console on application deployment (I've included both an image and the text as the text is proving difficult to format):
Text:
[2015-08-20 14:41:40.551] boot - 9908 INFO [localhost-startStop-1]
--- HttpMethodDirector: I/O exception (javax.net.ssl.SSLPeerUnverifiedException) caught when processing
request: SSL peer failed hostname validation for name: 46.4.112.4
[2015-08-20 14:41:40.551] boot - 9908 INFO [localhost-startStop-1]
--- HttpMethodDirector: Retrying request
[2015-08-20 14:41:40.795] boot - 9908 ERROR [localhost-startStop-1] --- HTTPMetadataProvider:
Error retrieving metadata from https://idp.ssocircle.com/idp-meta.xml
javax.net.ssl.SSLPeerUnverifiedException: SSL peer failed hostname
validation for name: 46.4.112.4
at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.verifyHostname(TLSProtocolSocketFactory.java:233)
at
org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.createSocket(TLSProtocolSocketFactory.java:194)
I have visited the url endpoint provided by ssocircle and the metadata is exposed.
If I visit the /saml/metadata endpoint of the service provider and get some helpful information: an org.opensaml.saml2.metadata.provider.MetadataProviderException exception. The description if which is "No IDP was configured, please update included metadata with at least one IDP"; however, the source of this may be the above described error.
QUESTION
Am I missing something that is readily apparent to start the example application? In other words, what does this error tell me that I need to be investigating? Or, as it is "non-breaking", do I ignore it?
WHY I'M ASKING
The documentation surrounding the deployment of the Sample Java Configuration application is minimal (as in "non-existant"). The self-documentation only provides "hints", such as the following:
// IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here
// Do no forget to call initialize method on providers
#Bean
#Qualifier("metadata")
public CachingMetadataManager metadata() throws MetadataProviderException {
List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
providers.add(ssoCircleExtendedMetadataProvider());
return new CachingMetadataManager(providers);
}
I am certain there is something I am not doing, particularly since I have not done anything in the deployment of the application except for the run of the mvn package, described above.
The problem occurs due to the sample application's utilization of a deprecated constructor - a deprecation whose warning was explicitly suppressed - for the HTTPMetadataProvider (a fix I will commit, shortly). In configuring the ExtendedMetadataDelegate, the two-parametered constructor is utilized:
#Bean
#Qualifier("idp-ssocircle")
public ExtendedMetadataDelegate ssoCircleExtendedMetadataProvider() throws MetadataProviderException {
#SuppressWarnings({ "deprecation"})
HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider("https://idp.ssocircle.com/idp-meta.xml", 5000);
// other config.s...
}
If replaced with the non-deprecated constructor that takes a java.util.Timer and an org.apache.commons.httpclient.HttpClient (in addition to the metadata url), the sample application works, beautifully, and no errors are logged.
Additional Non-OP-related Information
I had to do the below to get the Sample SAML app to run
After removing the deprecated constructor, I recommend doing two things:
Follow the steps outlined in 4.2.6 of the documentation, i.e. treat the application during setup as the XML-configured application. All the steps need to be taken to "register" the metada.The application will be unable to register its metadata with the current Java configuration (see below; point no. 2)
Change the default configurations in class WebSecurityConfig (read detail, below)
Configuration Change
In the configuration of the ExtendedMetadataDelegate bean ssoCircleExtendedMetadataProvider, change the ExtendedMetadataDelegate's property values as follows:
// code....
extendedMetadataDelegate.setMetadataTrustCheck(true);
extendedMetadataDelegate.setMetadataRequireSignature(false);
// code....
In the ExtendedMetadata bean (different from above), change the property values as below:
// code....
extendedMetadata.setIdpDiscoveryEnabled(true);
extendedMetadata.setSignMetadata(false);
// code....
"Disclaimer"
Whether or not this should be used in production, I do not know; however, it seems to better reflect both the XML-based configuration and resulting metadata of the XML-configured Service Provider example referenced in the SAML Spring Documentation
Just some hints:
I met this exception when I was trying to set up the HTTP-Artifact profile.
There is a hostnameVerifier in org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory (openws-1.5.1) and a verifyHostname() processing before OpenSAML tries to create socket to connect to other host.
I configured sslHostnameVerification to "allowAll" in org.springframework.security.saml.metadata.ExtendedMetadata, the allowed values are "default", "defaultAndLocalhost", "strict", and "allowAll".
It seems in your case, this exception was thrown when the SP (your saml sample) was trying to download the metadata.xml from IdP (ssocircle). The best way to figure out what is happening is to debug when and where the hostnameVerifier was set.
Or you can try set sslHostnameVerification to "allowAll" in the SSOCircle's ExtendedMetadataDelegate's ExtendedMetadata to have a try first..
checkout this answer: It basically describes a plugin I recently released that allows you to configure Spring Boot and Spring Security SAML this way:
#SpringBootApplication
#EnableSAMLSSO
public class SpringBootSecuritySAMLDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSecuritySAMLDemoApplication.class, args);
}
#Configuration
public static class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}
#Configuration
public static class MyServiceProviderConfig extends ServiceProviderConfigurerAdapter {
#Override
public void configure(ServiceProviderSecurityBuilder serviceProvider) throws Exception {
serviceProvider
.metadataGenerator()
.entityId("localhost-demo")
.and()
.sso()
.defaultSuccessURL("/home")
.idpSelectionPageURL("/idpselection")
.and()
.logout()
.defaultTargetURL("/")
.and()
.metadataManager()
.metadataLocations("classpath:/idp-ssocircle.xml")
.refreshCheckInterval(0)
.and()
.extendedMetadata()
.idpDiscoveryEnabled(true)
.and()
.keyManager()
.privateKeyDERLocation("classpath:/localhost.key.der")
.publicKeyPEMLocation("classpath:/localhost.cert");
}
}
}
There are a couple Demo apps integrated with SSO Circle

Categories