Block specific HTTP Get Requests in Tomcat - spring

I have Mapping URL of the for "/" and a controller
#RequestMapping(value = {"/index","/"})
public ModelAndView displaySlideShow() {
return new ModelAndView("index");
}
But because of this mapping requests like /... or /? or /.dfdhj also serves the homepage. I read it that Spring tries to match the best available url.
Is there a way to prevent such mapping ?

Related

Restrict API access by domain name [duplicate]

I have one war file for my application and I will be using 2 domains to access it. For example I want to access admin.jsp using admin.mydomain.com/adminpage and other jsp pages I want to access with local.mydomain.com.
Also, admin.jsp should be only accessible via admin.mydomain.com and not via local.mydomain.com. How to do this in spring-security / spring-mvc? Is there a support in spring framework for this?
Any help on this would be helpful. Thanks.
You can implement RequestMatcher, and maybe like
HostOnlyRequestMatch(String relativePath, String hostname)
and then override the boolean matches(HttpServletRequest request) method, and if the relativePath and hostname are same with request, return true.
Add the requestMatcher to http like this:
http
.authorizeRequests()
.requestMatcher(new HostOnlyRequestMatch("/admin", "admin.mydomain.com")).permitAll()
.antMatchers("/admin").denyAll();
One way would be to configure proxy (e.g. Nginx) to route your requests to your application server (e.g Tomcat) properly. Read here for more details https://www.nginx.com/resources/admin-guide/reverse-proxy/
You can get the requested url from request object in you mvc controller and if it is not form correct domain then you can throw or show proper error based on your project. Following is the code snippet
#Controller
#RequestMapping("/adminpage")
public class AdminPageController{
#RequestMapping(method = RequestMethod.GET)
public String getAdminPage(HttpServletRequest request) {
String url = request.getRequestURL().toString();
if(!url.contains("admin.mydomain.com")) {
throw RuntimeException("Not accessible through this domain.");
// You can implement your own logic of showing error here
}
}
}

Resolving POST /** request URL to full request URL using micrometer

With the micro-service architecture I have written a generic POST request handler which is consumed by all the micro-services. The post mapping in spring look like this:
#RestController
#RequestMapping(value = "/v1/", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
public class V1Controller {
#PostMapping(path = "/**")
public #ResponseBody Json post () {}
}
Now while I am consuming the metrics for this endpoint using micrometer I am only getting /v1/ as the endpoint in the metrics while I am sending the full URL like /v1/demo/foo from the calling service. I tried lot of the combination but it is not working. I have also added the WebMvcTagsProvider where I am listing to request and resolving the POST api calls.
#Bean
#SuppressWarnings("unchecked")
public WebMvcTagsProvider webMvcTagsProvider(ObjectMapper objectMapper) {
return new DefaultWebMvcTagsProvider() {
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
if ("POST".equals(request.getMethod())) {
Tag uriTag = Tag.of("uri", String.valueOf(request.getRequestURI()));
return Tags.of(WebMvcTags.method(request), uriTag, WebMvcTags.exception(exception), WebMvcTags.status(response));
}
return Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.exception(exception), WebMvcTags.status(response));
}
};
}
Still it is resolving to /v1/ URL in the metrics. I tried googling alot but didn't find any solution. Thanks in advance.
The build in Spring Boot RequestMapping based metrics match on the annotations and add those as tags.
This is to avoid a tag explosion. Imagine a #RequestMapping for a path like user/{userId}, you would want to group all those calls together (user/1, user/2, user/3).
You'll want to create your own Timer in your post method that set that url tags, etc there.
If you decide to reuse the same metric name as the built in Spring Boot metric, you'll want to disable that one as well, so you don't double count those requests.

How to filter request based on domain in spring-mvc

I have one war file for my application and I will be using 2 domains to access it. For example I want to access admin.jsp using admin.mydomain.com/adminpage and other jsp pages I want to access with local.mydomain.com.
Also, admin.jsp should be only accessible via admin.mydomain.com and not via local.mydomain.com. How to do this in spring-security / spring-mvc? Is there a support in spring framework for this?
Any help on this would be helpful. Thanks.
You can implement RequestMatcher, and maybe like
HostOnlyRequestMatch(String relativePath, String hostname)
and then override the boolean matches(HttpServletRequest request) method, and if the relativePath and hostname are same with request, return true.
Add the requestMatcher to http like this:
http
.authorizeRequests()
.requestMatcher(new HostOnlyRequestMatch("/admin", "admin.mydomain.com")).permitAll()
.antMatchers("/admin").denyAll();
One way would be to configure proxy (e.g. Nginx) to route your requests to your application server (e.g Tomcat) properly. Read here for more details https://www.nginx.com/resources/admin-guide/reverse-proxy/
You can get the requested url from request object in you mvc controller and if it is not form correct domain then you can throw or show proper error based on your project. Following is the code snippet
#Controller
#RequestMapping("/adminpage")
public class AdminPageController{
#RequestMapping(method = RequestMethod.GET)
public String getAdminPage(HttpServletRequest request) {
String url = request.getRequestURL().toString();
if(!url.contains("admin.mydomain.com")) {
throw RuntimeException("Not accessible through this domain.");
// You can implement your own logic of showing error here
}
}
}

Redirect after a POST vs redirect after a GET

I'm working on a Spring project. Here's my basic controller:
#Controller
public class Editor {
private static final String EDITOR_URL = "/editor";
#RequestMapping(value = EDITOR_URL, method = {POST, GET})
public ModelAndView edit(HttpServletResponse response,
HttpServletRequest request,
RedirectAttributes redirectAttributes,
#RequestParam Map<String, String> allRequestParams) {
// The code is trimmed to keep it short
// It doesn't really matter where it gets the URL, it works fine
String redirectURL = getRedirectUrl();
// redirectURL is going to be /editor/pad.html
return new ModelAndView("redirect:" + redirectUrl);
}
From web.xml:
<servlet-mapping>
<servlet-name>edm</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
I have jetty embedded and I'm trying an integration test:
#Test
public void redirectToEditPadSuccess() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(END_POINT + "/edm/editor")
.queryParam("param1", "val1")
.queryParam("param2", "val2");
HttpEntity<?> entity = new HttpEntity<>(requestHeaders);
HttpEntity<String> response = restTemplate.exchange(
builder.build().encode().toUri(),
HttpMethod.POST,
entity,
String.class);
HttpHeaders httpResponseHeaders = response.getHeaders();
List<String> httpReponseLocationHeader = httpResponseHeaders.get("Location");
assertTrue(httpReponseLocationHeader.size() == 1);
String redirectLocation = httpReponseLocationHeader.get(0);
URL redirectURL = new URL(redirectLocation);
assertEquals("/edm/editor/pad.html", redirectURL.getPath());
}
So when I execute the above it works fine and I get a green OK sign.
Now, the controller accepts both POST and GET methods. If I execute the test using GET method (replacing HttpMethod.POST with HttpMethod.GET), the result is going to be a 404.
The logs reveal:
WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/edm/editor/pad.html] in DispatcherServlet with name 'edm'
I tried to debug the application up to the DispatcherServlet and weird thing is that with GET, after the 302/redirect response the Dispatcher is being called again and turns this to a 200 - no idea how and why.
I'm going to try and explain what is going on, and then provide a solution.
First let's forget that you're running a rest case, and assume that the request is coming from a browser.
Scenario 1 : Browser issues a GET request, and the server responds with a redirect.
In this case, the browser reads the response status code as 302 and makes another request using the Location response header. The user sees a quick reload but doesn't notice anything wrong.
Scenario 2 : Browser issues a POST request, and the server responds with a redirect.
In this case, the browser does follow the response code and does issue a redirect, but, the second request is a GET request, and the original request body is lost in the second request. This is because strictly by HTTP standards, the browser cannot "re-post" data to the server, without an explicit request by the user. (Some browsers will prompt the user and ask them if they want to re-post)
Now in your code, RestTemplate is using what I presume to be a default HttpClientFactory, most likely this one: https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpRequestFactory.java.
This is how RestTemplate is handling the above two scenarios:
Scenario 1 : Rest Template issues a GET request, and the server responds with a redirect.
Here the Rest Template instance will work exactly as a browser would. That's the reason why two requests are being made, and the second one is looking for /edm/editor/pad.html
Scenario 2 : Rest Template issues a POST request, and the server responds with a redirect.
In this case, Rest Template will stop after the first call, because it cannot automatically override your request method and change it to GET, and it cannot prompt you for permission, like a browser would.
Solution: When creating an instance of RestTemplate, pass it an overridden version of the client factory, something like
new RestTemplate(new SimpleClientHttpRequestFactory() {
protected void prepareConnection(HttpURLConnection conn, String httpMethod) throws IOException {
super.prepareConnection(conn, httpMethod);
conn.setInstanceFollowRedirects(false);
}
});
This will instruct rest template to stop after the first request.
Sorry for the lengthy answer, but I hope this clarifies things.

Call servlet from Spring controller

I have a controller class with #ResponseBody annotation to server ajax request.
My requirement is to redirect to a servlet(post method) if a check fails. If check does not fails then i need to respond back with Json data
Class Controller {
#ResponseBody redirectTest() {
if(true) {
//send json data
} else {
//Redirect to a servlet
}
}
Is there a possible way to redirect to post method of servlet.
Is there any way to initialize the servlet on need basic, without creating any servlet mapping in web.xml.
Is there a possible way to redirect to post method of servlet.
Use the forward prefix
#ResponseBody
public String redirectTest(HttpServletRequest request) {
if (true) {
//send json data
} else {
request.setAttribute("someObj", someObj);
return "forward:/someServlet";
}
}
Is there any way to initialize the servlet on need basic, without creating any servlet mapping in web.xml.
For Servlet 3.0 or above, use the #WebServlet annotation

Resources