简体   繁体   中英

Spring-boot controller error when url contains braces character

I have a controller which receive requests as GET with 2 parameters e and p :

@GetMapping("")
public String getIframe(
    @RequestParam(value = "p", required = false) String p,
    @RequestParam(value = "e", required = false) String e
){
    System.out.println("Hi");
}

In some cases e and p values can contain {} characters. It was working well in spring-boot 1, but after updating the module to spring-boot 2 it started to raise an exception:

java.net.URISyntaxException: Illegal character in query at index 47: http://127.0.0.1:8080/shorteners/click/RikBV?e={CLICKID}&p={PUBID}
  at java.net.URI$Parser.fail(URI.java:2848)
  at java.net.URI$Parser.checkChars(URI.java:3021)
  at java.net.URI$Parser.parseHierarchical(URI.java:3111)
  at java.net.URI$Parser.parse(URI.java:3053)
  at java.net.URI.<init>(URI.java:588)
  at java.net.URI.create(URI.java:850)
  ... 45 common frames omitted
Wrapped by: java.lang.IllegalArgumentException: Illegal character in query at index 47: http://127.0.0.1:8080/shorteners/click/RikBV?e={CLICKID}&p={PUBID}
  at java.net.URI.create(URI.java:852)
  at fr.hamid.testApp.app.configurations.HeadersFilter.doFilter(HeadersFilter.java:43) [26 skipped]
  at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [9 skipped]
  at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
  at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
  at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
  at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
  at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [2 skipped]
 [1 skipped]

Also, My filter class is as follow:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HeadersFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        String referer = request.getHeader("referer") ;
        if (referer != null && new UrlValidator().isValid(referer)) {
            URL url = new URL(referer);
            response.setHeader("Access-Control-Allow-Origin", url.getProtocol() + "://" + url.getHost());
        }
        else {
            response.setHeader("Access-Control-Allow-Origin", "*");
        }
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Headers", "x-auth-token, x-requested-with, content-type, accept, " +
                "origin, referer, Authorization, customer-user-id, device-os, t-user-id, t-network-cache-capacity," +
                "t-network-bandwidth, device-client-date, dev-platform, sdk-version, sdk-type, app-package-name," +
                "device-imei, developer-key, device-model, device-os-version, secret-token, t-network-type");

        response.setHeader("Access-Control-Expose-Headers", "x-requested-with");
        response.setHeader("Access-Control-Allow-Credentials", "true");

        if (!"OPTIONS".equals(request.getMethod())) {
            //Exception raises here!
            chain.doFilter(req, res);
        }

        response.setHeader("X-Frame-Options", "*");
    }

    public void init(FilterConfig filterConfig) {
    }

    public void destroy() {
    }

}

When I use wget command it works well, but the exception raises when I call the url using browser!

Is there any way to handle these kind of urls?

Note: As customers are using these urls, I can not handle this problem by changing the {} characters with others. So, it is important to handle this server side!

Thanks a lot :)

Since spring boot 2.2.0 it is possible to configure Tomcat so, that special characters are allowed in URLs.

Just sum the ones you wanna allow in the application.properties

In your case that is

server.tomcat.relaxed-query-chars= {,}

all configurable chars are ` , { , } , [ , ] , ^ , < , > and |

In application.yml that would be

server:
  tomcat:
    relaxed-query-chars: ['{','}']

Assuming that you want the braces to show up in your params string. You will have to use HTML URL Encoding in the URL used in the browser, refer here

So your URL in the browser will look something like this:

http://127.0.0.1:8080/shorteners/click/RikBV?e=%7BCLICKID%7D&p=%7BPUBID%7D

Using code below to test and print:

@GetMapping("/urlParamTest")
public void getIframe(@RequestParam(value = "p", required = false) String p,
        @RequestParam(value = "e", required = false) String e) {
    System.out.println("Hi " + p + " , " + e);
}

Console output:

Hi {PUBID} , {CLICKID}

Hope this helps!

The url with {} in the query string is being rejected by tomcat as they are not valid url characters.

wget works because it urlencodes the querystring before sending it over {=%7B, }=%7D. The @RequestParam annotation results in a urldecoding, so you just see { and } in your variables. If you looked at the actual querystring you'd see %7B and %7D.

You can configure tomcat to be more relaxed about what characters it accepts, although it's best to get the clients to urlencode if you can.

Tomcat docs look for relaxedQueryChars, that's the option that allows you to tell tomcat to accept {} in the query string.

There are a few answers already about how to set this property, eg Setting a tomcat property using spring boot application properties

import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;

@Component
public class TomcatWebServerCustomizer
        implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector connector) {
                connector.setAttribute("relaxedQueryChars", "{}");
            }
        });
    }
}

Finally I could not solve the problem in spring boot and I had two choices:

1- Downgrade Spring-boot to the last version.

2- Add if statement in Nginx and encode the request parameters.

Although the second solution is not efficient and it probably slows down the serving requests, I choose that based on two reasons :

First of all, it was important for me to have Grafana to monitor my services and It was not possible with the old spring boot version.

Secondly, impacts on response time were negligible and I think it worth it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM