I use org.springframework.security.web.csrf.CookieCsrfTokenRepository to secure my spring based web application from CSRF attacks. This enables all the Controller methods to be called only by passing a X-XSRF-TOKEN header or a _csrf request parameter.
So in order for me get the response of a GET URL in browser, I will have to write something like the below in browser address bar.
http://localhost:8080/api/someresource?_csrf=99e3b824-d0c9-409d-91ee-c7ccbdce313f&filter1=value1&filter2=value2&so=on
However, Some of these urls have filter mechanism based on the request parameters and unfortunately this extra _csrf parameter breaks that logic.
As I see it, this request parameter should be needed if the request had passed the csrf validation. But I couldn't do anything in the documentation to remove the _csrf request parameter on the application level.
At the moment, I do something like below.
#ResponseStatus(OK)
#RequestMapping(method = RequestMethod.GET, value = "/search/advanced")
#ResponseBody
public ResponseVO advancedSearch( #RequestParam MultiValueMap<String, String> queryParameters, Pageable pageable) {
queryParameters.remove(MyApplicatonConstants.CSRF_PARAM_NAME); //this line is the hack that I wrote
return doStuffAndGetFilteredData(queryParameters);
}
This implementation has its drawbacks.
I will have to change all 143 controller methods to have this one line on the top.
I have to remember to add this for new methods in future.
it's a cheap hack and there should be some better and cleaner way of doing it.
Is there a way I can acheive this without rewriting that one line again and again?
Note:
I fully understand that I can use CURL or Postman so I can pass X-XSRF-TOKEN in header. But it's not as quick as opening the URL in a new tab.
Related
I'd like to create a controller which will return Page object. I will need page Pageable to have with page number and its size (it cannot be larger than 50) and some variables which will be search criteria. If this was a POST request, it could look as follows:
public Page<SomeDto) getDto(#RequestBody #Valid RequestDto requestDto, Page pageRequest)
However, it is a GET request so #RequestBody cannot/shouldn't be used.
Additionally, I would like to have something akin to #Valid so that Spring will automatically reject bad request.
I'm wondering whether:
it is possible to and if so how to implement this and have already created Dto from values taken from URL (#PathVariable or #RequestParam) as if it was POST request which maps body to object.
I'd like to avoid using some kind of that code:
public String updateFoos(#RequestParam Map<String,String> allParams) {}
if what is described in 1st is not possible, what solution would be closest as to meeting those requirements?
You can have a HTTP body (and therefore a #RequestBody) for every HTTP requests no matter what HTTP method is used. However, it is not a good REST API design to use a HTTP body for GET request (cf. https://martinfowler.com/articles/richardsonMaturityModel.html)
You can do bean validation with #PathVariable or #RequestParam arguments, you just have to put the annotation you want next to these annotations
I've been using a generic map for query parameters in a spring-boot (4.3.3.RELEASE) application, which has been live and taking traffic for a while now. This application has dozens of different endpoints and recently I've seen that a number requests result in errors due to improperly formatted query parameters.
I've been seeing a significant number of the requests are coming through like http://web.com/url/path?param1=1?param2=2. Unfortunately, I don't control or know why these urls are showing up, and they are all resulting in error pages for the users using them.
Can spring be customized to parse maps of query parameters using both '&' and '?' as separators?
I suspect this can be supported with a custom interceptor to inspect modify the incoming urls, but I wanted to know if there is an easier solution.
This is an example the one endpoint
#RequestMapping(value = "/url/path", method = {GET, POST})
public ResponseEntity<Void> handleRequest(
#RequestHeader HttpHeaders requestHeaders,
#RequestParam MultiValueMap<String, String> requestParams) throws Exception
{
....
}
I am currently working on a monitoring application using Spring Cloud Sleuth. Currently I try to collect as much information about my requests as possible.
To keep the approach as scalable as possible I use GenericFilterBeans and HandlerInterceptorAdapter to access information from the requests sent to the REST-API.
I am struggling with getting parameters of a REST-call where the parameters are mapped from the URL like in the following code snippet:
#RequestMapping(
value = {"/{service}/{route_id}/book", "/accounting-core-service/{service}/{route_id}/book"},
method = RequestMethod.GET)
#ResponseBody
public ModelAndView book(#PathVariable(value="service") String serviceName,
#PathVariable(value = "route_id") int routeId,
HttpServletResponse response) {
/*Do something*/
}
The question is not, whether it is good practice or not to write it like so. The question is whether there is an approach similar to Filter or Interceptor (or the proper use of them) to access those parameters.
A requirement is, that it can be applied easily to an application by adding very few lines of code. Annotating every Method call manually or manually inserting the code to write the parameters into the trace from within the method is not feasible for me.
If you need more information feel free to ask. I will provide you with all information you need to help me with my problem.
Although not officially supported (as it's not written in the reference documentation), Spring MVC holds that information as request attributes.
You could create your own HandlerInterceptor, ordered right after the Sleuth one, and get that information from the request like this:
// "/{service}/{route_id}/book"
String matchingPattern = (String) request
.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
// "service" => "fooService", "route_id" => "42"
Map<String, String> templateVariables = (Map<String, String>) request
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
Note, the template variables are already decoded at that point, which is probably what you want anyway.
You can create a Filter that intercept all the requests.
For each request you can retrieve also this informations:
query parameters
body of request
url
header parameters
You can save all this data as you need.
This is the best way you can do that.
If you need to group all urls like /{service}/{route_id}/book in a "family" of urls you can do that splitting the url and check if it is part of the family, but when a new family is added in your code you need to update the filter (or configure something in an external file or database).
I have tried to find the answer to this, but I cannot seem to find what I am looking for. So I apologize if this question already exists.
PROBLEM:
I want to be able to access the request type of a request inside of a generic method within my Controller.
DESCRIPTION:
Using Spring ROO and Spring MVC, I have developed a small web service that will respond with certain tidbits from a database when queried. In one of my controller classes, I have some methods that handle some variety of GET, PUT, POST, etc., for the URIs that are mapped within the #RequestMapping parameter.
For example:
#RequestMapping(method = RequestMethod.Get, value = "/foo/bar")
#ResponseBody
public ResponseEntity<String> getFooBar() {
// stuff
}
If a request is made to the web service that it is not currently mapped, a 405 error is returned (which is correct), but I want to return more information along with a 405 response. Maybe respond with something like:
"I know you tried to execute a [some method], but this path only handles [list of proper methods]."
So I wrote a short method that only has the RequestMapping:
#RequestMapping(value = "/foo/bar")
I have found that the method with this mapping will catch all unhandled request types. But I am having trouble accessing the information of the request, specifically the type, from within the method.
QUESTION:
A. How can I access the request type from within the method? OR
B. Is this the right approach? What would be the right approach?
EDIT
ANSWER:
I added a HttpServletRequestobject to the method parameters. I was able to access the method type from that.
I tried using HttpRequest, but it didn't seem to like that much.
Thanks all!
You can add a method parameter of HttpServletRequest, but I think you'd be better off continuing to reply with 405. A client should then make an HTTP OPTIONS call (see How to handle HTTP OPTIONS with Spring MVC?) and you can return the list of allowed methods there.
A. you can access request if you mentioned it as parameter in controller method
public ... getFooBar(HttpRequest request) {
...
}
B. you do not need to add any other description as the 405 status is descriptive.
In answer to "A", just add "HttpRequest req" as an additional argument to your controller methods. Spring will automatically inject a reference to the request, and you can play with headers to your heart's content.
In answer to "B" - "What would be the right approach", how about this?
In order to return that 405, Spring has raised a MethodArgumentNotValidException. You can provide custom handling for this like so:
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public MyMethodArgumentMessage handleMathodArgumentNotValidException(
MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
MyMethodArgumentMessage myMessage =
new MyMethodArgumentMessage(result.getFieldErrors());
return myMessage;
}
You should take a look at the #ExceptionHandler annotation. This lets you add methods such as the following to your controller. You can define your own exceptions and appropriate custom handlers for them. I use it to return well-structured XML and JSON from REST services. Although for it to work, you need to throw specific exceptions from your controller methods.
A good walk-through of using this was provided by Petri Kainulkainen in his blog:
http://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-adding-validation-to-a-rest-api/
I have a somewhat simplistic controller configured as thus:
#RequestMapping(value = "user/savearticle", method = RequestMethod.POST)
public #ResponseBody
Object saveArticle(#ModelAttribute("article")RawArticle rawArticle);
Using snippets of code taken from here, I made a test case for the controller that looks like this:
MvcResult resultActions =
mockMvc.perform(MockMvcRequestBuilders.post("/user/savearticle")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.content(convertObjectToForumUrlEncodedBytes(rawArticle)))
.andReturn();
and I simply print out the result. In any case, the ModelAttribute "rawArticle" keeps ending up as null when it enters the controller's implementation, however when I use this:
MvcResult resultActions = mockMvc.perform(
MockMvcRequestBuilders.post("/user/savearticle")
.param("title", rawArticle.getTitle())
.param("tags", rawArticle.getTags())
.param("body", rawArticle.getBody())
.param("author", rawArticle.getAuthor())).andReturn();
the mapping actually works like a charm. What I want though is that the first test be processed correctly as it seems so wrong that it's not being mapped as I thought it should be, similarly the controller is primarily being used by another program over the network using apache http (which somehow automatically passes a urlencoded form).
Do you guys have any idea where I could've made an error? I wouldn't mind posting snippets of my context configuration if you think you need it to evaluate the problem (or my pom for that matter, but just telling me what libraries I may have missed should be enough)
Update:
I made a mistake of inserting the POJO into a session in test number 1, I simply removed it here. The question stands the same.
The content is the content body of the request, you are using a content type which expects everything encoded in the URL not in the content body. Spring does data binding based on the request parameters if you want to use the content body you have to use #RequestBody instead of #ModelAttribute.