We have an application build on Java 1.6 with Spring 3.0.3 that use Spring Security 3.0.5 and implements REST API using Spring Web with RestEasy 2.1.0.
I need to place this application (server) behind a proxy that would translate HTTPS request traffic from a REST API Client application into HTTP traffic. This change creates a “cross domain” scenario for login request : Client sends HTTPS request for login, and Server answers with redirect URL of HTTP. Currently it responses with:
”http://192.168.0.10:8090/index.html;jsessionid=64FD79...86D”,
what I need here is:
”/index.html;jsessionid=64FD79...86D”
We came with solution to make server to respond with “relative” URL instead “absolute” URL. So I tried to implement something similar with described situation here:
thread on forum.spring.io
I have set the RedirectStrategy bean with contextRelative="true" and override the redirectStrategy setter from AbstractAuthenticationTargetUrlRequestHandler within my LoginSuccessHandler extended class and I see that redirectStrategy property for HttpServletResponse object is set to true as expected. Still it not resolving the issue.
Also when changing redirectURLCC property of HttpServletResponse object using encodeRedirectURL("otherLogin") is sets something like
”http://192.168.0.10:8090/otherLogin”
and its not what I need. I need to remove whole protocol+ipaddress part of the URL. The URL property of response object is not accessible for change as it is wrapped by Filter and FilterChain interfaces implementation.
Please suggest any ideas. I suppose this kind of things should be resolved in web.xml or auth-AplicationContext.xml files not in code.
Best Regards.
Spring Security uses the following logic when sending a redirect:
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {
String redirectUrl = calculateRedirectUrl(request.getContextPath(), url);
redirectUrl = response.encodeRedirectURL(redirectUrl);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to '" + redirectUrl + "'");
}
response.sendRedirect(redirectUrl);
}
The sendRedirect method is required to behave in the following way:
This method can accept relative URLs; the servlet container must
convert the relative URL to an absolute URL before sending the
response to the client.
That means you will by deafult always get an absolute URL, no matter what's the configuration or context setting.
You have multiple options:
configure your container or application server to be aware of the public URL (for example by using AJP instead of HTTP reverse proxy, or passing HTTP headers with the public URL which is supported by some application servers), e.g. documentation for Tomcat
configure your HTTP reverse proxy to perform correct rewriting, e.g. see ProxyPassReverse in Apache mod_proxy documentation
implement a custom org.springframework.security.web.RedirectStrategy where you will manually set the Location response header and HTTP 302 status code, this should allow you to send context relative redirect as you want
Not entirely relative, but probably you might want to have a look into a 4th option using the org.springframework.web.servlet.view.UrlBasedViewResolver to rewrite the redirected URL with your external hostname as described in this answer: Override the default redirect URL in a SpringMVC application.
That should be achievable on Apache with:
ProxyPreserveHost Off
ProxyPass / http://192.168.0.10:8090
ProxyPassReverse / http://192.168.0.10:8090
and adding one more proxy reverse on the proxing host with the port.
E.g supposing your server name is proxy.example.com the fourth line would be:
ProxyPassReverse / http://proxy.exemple.com:8090
Look at this answer: Sending redirect in Tomcat web application behind a Apache 2 proxy (mod_proxy)
Related
I have a java application running in a tomcat. In one endpoint of the application I want to return a HTTP-response of type See-Other.
#GET
#Path("logout")
#Produces(MediaType.TEXT_HTML)
public Response logOut(#Context HttpServletRequest request) throws URISyntaxException {
// logout logic omitted
Response.ResponseBuilder response = Response.seeOther(new URI("../jsp/login.jsp"));
return response.build();
}
When I test this on my local machine everything works fine. On the production system however jersey seems to be unable to resolve the correct hostname from the relative path "../jsp/login.jsp". Although the host is called myhost the relative path is resolved as http://localhost:7080/myapplication/jsp/login.jsp.
new URI("../jsp/login.jsp") returns an Object where all fields are null and only path="../jsp/login.jsp". For this reason the root of the behavior must be in the Response class. I checked the documentation and figured that Response.seeOther makes a call to the Response.location method. The documentation states that
If a relative URI is supplied it will be converted into an absolute
URI by resolving it relative to the base URI of the application
Does anyone know how Jersey determines the base URI of the application? Is there some config file in the tomcat or an environment variable I set wrong?
The base URI of the application is computed using the properties of the HttpServletRequest object and is of the form:
<scheme>://<serverName>:<serverPort>/<contextPath><servletPath>/
The <serverName> and <serverPort> are usually supplied by the HTTP client, while the rest is fixed by Tomcat's or your application's configuration:
<scheme> is configured using the homonymous attribute of a <Connector> (cf. Tomcat's documentation) or can be set by the RemoteIpValve (cf. documentation),
<serverName> is supplied by the HTTP client, but can be overridden using the proxyName attribute of a connector,
<serverPort> is supplied by the HTTP client, but can be overridden using the proxyPort attribute of a connector or by the RemoteIpValve,
<contextPath> depends on how you deploy your application (e.g. it depends on the name of the WAR file in the webapps directory),
<servletPath> is configured in the web.xml descriptor or through the #ApplicationPath annotation.
What probably happens in your production environment is that Tomcat is behind a reverse proxy. To configure the base URI correctly in such a situation you have two choices:
Statically configure the correct values of scheme, secure, proxyName and proxyPort on your <Connector>,
Use the RemoteIpValve and configure the proxy server to send the original Host header and add the X-Forwarded-For and X-Forwarded-Proto headers.
If your proxy forwards different kinds of requests to Tomcat (e.g. both HTTP and HTTPS requests), the RemoteIpValve is your only choice.
Remark: The base URI can also be based on the request URI if you set the ServerProperties.LOCATION_HEADER_RELATIVE_URI_RESOLUTION_RFC7231 property to true: cf. this question.
When i call the context root of my spring-boot application "localhost:8080/api/players", which is mapped in a RestController method by the annotation #GetMapping(path= {"/",""}), undertow alway redirect (httpstatus: 307 Temporary redirect) to "localhost:8080/api/players/" adding trailing slash at the end.
My application context-root is indeed /api/players as defined in spring-boot application.properties file (server.servlet.context-path=/api/players)
I've tried with embedded-tomcat and it works correctly by setting the property server.tomcat.redirect-context-root=false
There is a way to configure undertow to act like tomcat?
Peering into the sourcecode for undertow a bit, it looks like the relevant code is here in ServletInitialHandler.java, which will issue a 307 redirect status code in the case that the request is an upgrade request... unless the request is an HTTP 2 upgrade request. This doesn't seem to be configurable by the server, although there is some attempt to avoid the redirect based on what the client does.
It's probably worthwhile to look at your HTTP requests, understand better if your HTTP client is actually making an upgrade request, and then consider either accepting this fact, or changing the client to make a different kind of request (possibly by making it send the HTTP2 upgrade request header).
I am using spring security login mechanism for my application and tested everything.Things were working fine.I have the following use case
If customer is not logged in , application will redirect customer to the login page.
On successful login, application will redirect customer back to same page from where they were redirected to the login page
this is the Java code used to redirect user to his original location
final SavedRequest savedRequest = this.requestCache.getRequest(request, response);
targetUrl = savedRequest.getRedirectUrl();
getRedirectStrategy().sendRedirect(request, response, targetUrl);
RedirectionStrategy being used here is DefaultRedirectStrategy, things were working fine. Application is now deployed on the Pre Production server and now this seems not working and I am getting 404 error.
When customer is being redirected to the home page,targetUrl is coming out as "/", I have a Spring controller named with this mapping
#RequestMapping("/")
public class HomePageController{ // home page code }
my application's current Pre-Prod urs is prepd-www.mysite.com so when sendredirect come in to action, webpage URL is getting changed to prepd-www.mysite.com/prepd-www.mysite.com
I am not sure what is causing this issue. is it because of the proxy server settings ?
Can any one suggest me about the possible root cause of this issue?
I have already tried it on all local machines and well on our QA but everything is working perfectly fine.
Current setup for the environment where this is happening is
We have 4 app server
We have one load balancer which is redirecting traffic to one of the app server.
Just a wild guess since you could not give the reverse proxy configuration, nor the exact URL used in pre prod and in developpement.
You say you are using DefaultRedirectStrategy from Spring security. This strategy has an option (contextRelative) that prepends the ServletContext path to the URL. If in your developpement system you were using the root context, that is if you were accessing home page at (for example) : http://localhost:8080/ the serlet context was empty.
But if now in preprod, the servlet context is no longer root but is say /myApp once translated by apache reverse proxy, when you redirect you get an URL of /myApp/myApp that could be translated back to what you gave.
You could try to control whether you have contextRelative as true in DefaultRedirectStrategy and if yes if you can set if to false and also control if you redirect to absolute or relative URLs.
If you are using apache in front check rewrite rule and redirect rules of apache config. Best way would be to ssh tunnel directly to application server(by skipping apache) and test. If it's working that means your application config is fine and it needs to be fixed in apache.
Are you using in preproduction tomcat or another application server?, normally if your war is calling foo and your commit to tomcat, the path for this war is
http://localhost:8080/foo/
So if you are using servlet you need specify in your web.xml that the main path is foo/*
The use case I am trying to implement.
Display different content based on the {{random_string}} in the URL path.
Users will see different content based on the {{random_string}} that the URL contains.
eg:
www.example.com/{{random_string}}/index.jsp
The URLS will look like these below. (
They include random characters before the JSP)
www.example.com/xc/index.jsp www.example.com/2b/index.jsp
www.example.com/43/index.jsp
My question
How do I setup nginx and tomcat to be able to support the {{random_string}} in the URL without throwing 404?
My Current Environment/Setup (this works fine)
Nginx along with Tomcat.
Requests that come to nginx are then redirected to tomcat to access ROOT.war
e,g - www.example.com/index.jsp
You shouldn't have to change anything in Nginx or Tomcat config. What you could do is to create a servlet that will intercept the requests and extract the {{random_string}} before forwarding to the JSP. Here are the basic steps:
1) Create a servlet with a URL pattern of /* so that all requests go to it.
2) In the servlet's doGet() method, use request.getPathInfo() to retrieve the URL path and parse it to extract the {{random_string}}.
3) Use request.setAttribute() to set attributes for the data you want to display in the JSP page.
4) Forward the request to the JSP using a RequestDispatcher, e.g.:
RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp");
dispatcher.forward(request, response);
5) In the JSP, use the request attributes that you have set in step 3 to display the content.
i am running a tomcat server which has filters (and a servlet) mapped to /xxx/*
I have client that sends //xxx/* at the HTTP header as the uri. as a result, the servlet and the filters are not getting called.
I have tried putting a filter at /* that catches the request, wraps it with a requestWrapper and override the getURI() and getServletPath() methods (they just return the URI with a single / to whoever calls)
That doesn't seem to work, so i am assuming that once tomcat receives a request it decides which servlet/filters should be evaluated against this uri BEFORE even sending it to the first filter.
Is there a way to solve this? can i make tomcat reevaluate after every filter maybe? is there another way?
thanks in advance
If the URLs in the request header are not conforming to the spec, Tomcat is doing the right thing by dropping them.
If this is the problem, you need to fix the client so that it puts proper absolute URLs into the HTTP requests.
"Hacking" Tomcat to make it accept rubbish requests is a bad idea. It will limit your options for upgrading platforms, and/or deploying in different network environments.