Problem: I make a request that requires authentication. OAuth server will save the original request and redirect to "/login". I need to pass a query parameter from the original request to the login form (I need this before the form is submitted in order to filter to the correct AuthenticationProvider).
Trying to filter on super(new AntPathRequestMatcher("/login", "GET")); is too late. The ServletRequest is already a redirect to login. Therefore, I tried to create a custom auth entry point which extended LoginUrlAuthenticationEntryPoint. I simply did an Override on determineUrlToUseForThisRequest to append my query from the original request. This idea worked for the URL's sake, but unfortunately Spring's /login page does not show up unless the URL is exactly "/login".
Any idea on how to work around this would be greatly appreciated!
EDIT/Update
This is not yet tested but — if I use a custom AuthenticationEntryPoint I can redirect to a custom login page endpoint. This endpoint would take in a #RequestParamand be put into a hidden field on the login form. Then I can POST with that new field added to WebAuthenticationDetailsSource. From here, my POST filter should correctly choose a provider.
I configured my setup as stated above in my Edit/Update. It works.
1) I configured a CustomAuthenticationEntryPoint
2) Did a #Override protected String determineUrlToUseForThisRequest()
to build my login string with a query parameter passed in from client's request.
3) LoginController does this
#GetMapping("/login")
public ModelAndView showCustomLoginForm(#Valid #RequestParam(value = "realm_name", required=false) final String realmName) {
CustomLoginForm form = new CustomLoginForm(realmName);
return new ModelAndView(CUSTOM_LOGIN_FORM_VIEW, CUSTOM_LOGIN_FORM_MODEL, form);
}
where realm_name is my hidden field in the form that I need upon POST.
Related
I went through so many links like How to show all controllers and mappings in a view and How to configure a default #RestController URI prefix for all controllers? and so on.
I want to get the Request Mapping URL at Filter interceptor
Ex: This URL I configured at REST controller method, and naturally we will pass /employees/employee-names/John to get the Employee John.
/employees/employee-names/{employee_name}
Now, when somebody hit /employees/employee-names/John I want to get the value of actual mapping url if REST controller /employees/employee-names/{employee_name},
Any pointers how to get that ?
Spring MVC sets the attribute HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE, which you can use to get the pattern that was used to match the incoming request:
String matchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)
That would return /employees/employee-names/{employee_name} in your case.
I was able to solve this issue using below code. AntPathMatcher is the perfect way to identify if the incoming request and URL you configured in the property file matches exactly. This solution works greatly for me.
AntPathMatcher springMatcher = new AntPathMatcher();
Optional<String> antMatch = props.getMapping().stream()
.filter(//Perform Some Filter as per need)
.map(Mapping::getVersion)
.findFirst();
return antMatch.isPresent() ? antMatch.get() : null;
I have a rest api set up at api/books, and one can send a new book object there, and it will be added to the database. The guestion is, how can I correctly catch what is being POST'ed, so one can for example, validate what is being sent?
#RequestMapping(value="/api/books", method = RequestMethod.POST)
public String bookSavePost(#RequestBody Book book) {
bookRepository.save(book);
return "redirect:/api/books";
}
This works, as in it saves the book and I can catch what the user sends, but with this enabled, it overrides the default REST method, which doesn't allow one to actually view the api anymore. If I change this particular method to GET, it returns a string "redirect:/api/books", so it doesn't even redirect anything. Is there some way to actually redirect it to the rest api endpoint?
You can write your own reuquest Interceptor .
Spring provides HandlerInterceptor class :
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/HandlerInterceptor.html
Here is a quick sample how to do this:
https://www.baeldung.com/spring-mvc-handlerinterceptor
A redirect requires three two things to be successful: A HTTP code of 301 or 302 AND a location header that includes the endpoint to which you want the client to visit.
E.g., in the headers section you should have
location: '/correct/endpoint'
The scenario:
a User class has several groups of properties: password, address, preference, roles.
We need different Ajax calls to update the (1) user password, (2) user profile, (3) roles a user is in.
All the tutorials and examples only shows one POST action to update the whole User class. My question is how we can update only part of the class.
For example, when updating the user password, we will:
Display a text box to collect new password from user input.
Make an Ajax call that only POST the new password together with the userId (like: {id=3, newPassword=xxxxx}) to the WebAPI POST action.
That action will only update the password for the user.
One solution: (the easiest to think of)
Call the GET action with the userId to retrieve all the data for a user
Update the password in the user data with the values obtained from the web user input
Call the POST action with the updated data, which contains all properties in the User class.
That POST action will update the whole data without knowing only the password is changed.
The benefit: only one POST action is needed for the ApiController.
The shortcoming: we have to Ajax twice.
So, is it possible that we can have multiple POST actions in one ApiController? For example, PostPassword(userId, password), PostProfile(userId, profile) and PostRoles(userId, roles).
In this way, we will only call PostPassword to send the password to ApiController. In client side, there will be only one Ajax call. It is on the server where we will do the update. The benefit is of course the reduced data transferred over Internet.
If it is possible, what is the correct way to direct all different POST calls to their corresponding actions in the ApiController?
Please help us. Thank you all.
Most of cases, needless to have muptile post actions, I think. The typical case is consumer needs to edit user. So, s/he needs to load user data first to show on the edit form. After editing, consumer can click Save button to submit data and call POST action on api controller.
If your case is different, you should have nullable property for value type, and then the logic which should be checked in controller is if any property is null, it should not update this property into database.
You can only have one post action per controller action name. That is, you cannot do
// NOT VALID:
public ActionResult UpdateUser(string newPassword) { }
public ActionResult UpdateUser(List<string> newRoles) { }
However, parameters of the action can certainly be nullable. If a given property is not supplied in a given HTTP request, the value of the property in the controller would be null.
// VALID:
public ActionResult UpdateUser(string newPassword, List<string> newRoles)
{
if (newPassword != null) { } // It must have been supplied
if (newRoles != null) { } // It must have been supplied
}
Alternatively, you can have related controller actions that each handle one of your use cases, e.g. UpdatePassword(...), UpdateAddress(...), ...
I guess I'm looking more for advice than any specific coding solutions. Here's the scenario:
I have a form where a new user can be created
This form is accessed via a GET request through a Spring controller
#RequestMapping(value = "/secure/clients", method = RequestMethod.GET)
public String prepareNewClient(final Principal principal, final ModelMap map) {
map.addAttribute("client", new Client());
return "secure/clients";
}
The form is presented, all works fine and I submit the new client using $.ajax({})
The submission triggers a POST request using the same URL to the following method on my controller
#RequestMapping(value = "/secure/clients", method = RequestMethod.POST)
public #ResponseBody JsonResponse saveClient(
#ModelAttribute("client") final Client client,
final BindingResult result,
final Principal principal,
final ModelMap map) {
// validate input
// save client
// prepare JsonResponse object
return jsonResponse;
}
After completion, I want to keep the user on the same screen and have the form trigger a PUT instead of a POST. Which is fine, I can achieve that with jQuery, but as soon as I submit the form again, the client is not on the ModelMap anymore.
I have even tried adding the saved client to the ModelMap of my POST method but even that didn't work.
I'm not entirely sure if I'm doing it the right way. To be honest, every tutorial I've seen uses more or less what I'm doing but NONE of them have a PUT request - mostly deal with creation of objects, which I don't have an issue.
What I have in mind is that I might need to have a controller method mapping to /secure/clients/{clientId} with HTTP.GET and another controller method mapping /secure/clients/{clientId} with HTTP.PUT.
But I'm not sure if that makes sense, so I'm a bit lost.
Should I create my controllers entirely on JSON calls?
Should I redirect the user and forget about AJAX calls in this scenario?
Suggestions are more than appreciated. Thank you!
HttpMethod are not enterely support by web browsers. PUT requesr works great when two applications are working with Restfull API's, but for browser compatibility, it is better to limit yourself to POST or GET request.
You can also put a override parameter in your form to instruct the web server to handle the post as the desired method.
<input type="hidden" name="_method" value="put"/> would do the job.
Spring sf namespace takes care of that hidden field for you.
<sf:form method="put">
....
</sf:form>
In addition, you can test your controller with restclient-ui to see if the problem comes from your WebServer or your client.
I agree with #Daniel.
But you can use HiddenHttpMethodFilter available in Spring. That should make your work easy. You will no longer need to put the hidden filed every time.
Hope that helped. Cheers.
#Daniel and #Japan thank you very much for your answers.
I figured what you said #Daniel and I ended up stepping out of the box and thinking differently - and it worked great.
Just to give you an idea:
Instead of staying on the page, when a new client is inserted I actually refresh the browser
After the function is called the user gets redirected to /secure/clients/{clientId}
From there on a new function is mapped, along with a new POST request
It worked for me. And leveraging from Knockout.JS helped a lot as well.
Thanks again!
i tried to search for any previous post related to my issue but couldnt find any. I have a scenario where in page handles 3 different scenarios and one of them not working. This page returns different content depending on if the user is authenticated or anonymous.
localhost:8080/myApp/muUrl?test=authenticatedContent - > used for Scenario 1 & 2
localhost:8080/myApp/muUrl?test=anonymousContent -> used for Scenario 3
Scenario:
1) Authenticated user accesing the page url - the user gets displayed correct information. Works fine
2) Anonymous user accesing page URL with parameters that requires authentication - If anonymous, there is second level of check on the content they are accessing. for example, based on the GET parameters, there is custom logic to determine if the user has to be authenticated. In which case the page gets redirected to login page (WORKS fine).
3) Anonymous user accessing page URL with parameters that doesnt need authentication - in this case i get the SAvedRequest and redirect to the URL which is taking me to an infinite loop.
Am i missing something very obvious or is there a way in AuthenticationProcessFilterEntryPoint to say "DON'T redirect to LOGIN page but process it" ?
thanks.
I found a solution at last (someone suggested it to me on the Spring forums).
The idea is to use the #PreAuthorize annotation in the controllers as described here: see here
See code sample below:
#RequestMapping("/")
#PreAuthorize("isAuthenticated()")
public String authenticatedHomePage() {
return "authenticatedHomePage";
}
#RequestMapping("/")
public String homePage() {
return "homePage";
}