If I am using two #InitBinder in a one controller then how it belongs to two particular BindingResult? - spring-mvc

Below code in my controller class.
#Controller
#RequestMapping("/library/*")
public class HelloController {
#Autowired
#Qualifier("booksValidator")
private Validator booksValidator;
#Autowired
#Qualifier("loginValidator")
private Validator loginValidator;
#InitBinder("login")
private void initUserBinder(WebDataBinder loginBinder) {
loginBinder.setValidator(loginValidator);
}
#InitBinder("book")
private void initBooksBinder(WebDataBinder booksBinder) {
booksBinder.setValidator(booksValidator);
}
#RequestMapping(value="welcome", method = RequestMethod.POST)
public String printWelcome(#Validated #ModelAttribute("user") User user, BindingResult login, ModelMap model) {
}
#RequestMapping(value="add", method = RequestMethod.POST)
public String addBooks(#Validated #ModelAttribute("books") Books books, BindingResult book, ModelMap model) {enter code here
}
}
#InitBinder("login") belongs to--> BindingResult login
#InitBinder("book") belongs to--> BindingResult book
How I can do?
Please suggest me...
:(

A Spring controller can have multiple #InitBinder methods. But you are not using correctly the value of the annotation. According to InitBinder javadoc about the value parameter, Specifying model attribute names or request parameter names here restricts the init-binder method to those specific attributes/parameters
So in your example, you should use ModelAttribute names and not BindingResult parameter names, that is #InitBinder("user") and #InitBinder("books") instead of (resp.) #InitBinder("login") and #InitBinder("book").

i would suggest you split this into two separate controllers. One that deals with your users/login and the other to deal with books. So, something like LoginController that has the init binder for login, and BooksController that has the book validator for validating books.

Related

How to make #PreAuthorize having higher precedence than #Valid or #Validated

I am using spring boot, and I have enabled the global method security in WebSecurityConfigurerAdapter by
#EnableGlobalMethodSecurity(prePostEnabled = true, order = Ordered.HIGHEST_PRECEDENCE)
And Below is my controller code
#PreAuthorize("hasAnyRole('admin') or principal.id == id")
#RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public User updateUser(#PathVariable("id") String id, #Valid #RequestBody UserDto userDto)
{ ....}
However, when a non-admin user try to do a PUT request, the JSR303 validator will kick in before #PreAuthorize.
For example, non-admin user ended up getting something like "first name is required" instead of "access denied". But after user supplied the first name variable to pass the validator, access denied was returned.
Does anyone know how to enforce the #PreAuthorize get checked before #Valid or #Validated?
And I have to use this kind of method-level authorization instead of url-based authorization in order to perform some complex rule checking.
I had the same issue and I found this post. The comment of M. Deinum helps me to understand what was going wrong
Here is what I did :
The public method has the #PreAuthorize and do the check
There is NO #Valid on the #RequestBody parameter
I create a second method, private, where I do the DTO validation. Using the #Valid annotation
The public methods delegates the call to the private one. The private method is called only is the public method is authorized
Example :
#RequestMapping(method = RequestMethod.POST)
#PreAuthorize("hasRole('MY_ROLE')")
public ResponseEntity createNewMessage(#RequestBody CreateMessageDTO createMessageDTO) {
// The user is authorized
return createNewMessageWithValidation(createMessageDTO);
}
private ResponseEntity createNewMessageWithValidation(#Valid CreateMessageDTO createMessageDTO) {
// The DTO is valid
return ...
}
For the same scenario, I have found reccomendations to implement security via spring filters.
Here is similar post : How to check security acess (#Secured or #PreAuthorize) before validation (#Valid) in my Controller?
Also, maybe a different approach - try using validation via registering a custom validator in an #InitBinder (thus skip the #valid annotation).
To access principal object in filter class:
SecurityContextImpl sci = (SecurityContextImpl)
session().getAttribute("SPRING_SECURITY_CONTEXT");
if (sci != null) {
UserDetails cud = (UserDetails) sci.getAuthentication().getPrincipal();
}
In this case /{id} is a path param in the URL. To access path params in filter or interceptor class:
String[] requestMappingParams = ((HandlerMethod)handler).getMethodAnnotation(RequestMapping.class).params()
for (String value : requestMappingParams) {.
Use WebSecurityConfigurerAdapter.configure(HttpSecurity http) instead of #PreAuthorize
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.mvcMatchers( "/path/**").hasRole("admin");
}
}

How to create domain entity object from entityForm?

In Broadleaf Commerce framework, I am looking for a way to pull domain entity objects like product, admin user from entity form that we receice as model attribute in CRUD methods of EntityController classes like AdminProductController. Is it possible? If yes, how?
Below is a sample code from AdminProductController class.
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addEntity(HttpServletRequest request, HttpServletResponse response, Model model,
#PathVariable Map<String, String> pathVars,
#ModelAttribute(value="entityForm") EntityForm entityForm, BindingResult result) throws Exception {
//some code
}

Mapping custom data type from URL to Spring MVC controller using #ModelAttribute

I am trying to send parameters from UI to Spring MVC controller. My parameter looks like
caseId=23951910&serviceProvided%5B0%5D.id=25989&serviceProvided%5B0%5D.desc=24-Hour+Service&serviceProvided%5B0%5D.duration=1&serviceProvided%5B0%5D.pages=--&serviceProvided%5B1%5D.id=25988&serviceProvided%5B1%5D.desc=3rd+Party+Contact&serviceProvided%5B1%5D.duration=2&serviceProvided%5B1%5D.pages=--&serviceProvided%5B2%5D.id=25980&serviceProvided%5B2%5D.desc=Advice&serviceProvided%5B2%5D.duration=3&serviceProvided%5B2%5D.pages=--&serviceProvided%5B3%5D.id=25982&serviceProvided%5B3%5D.desc=Document+Preparation&serviceProvided%5B3%5D.duration=4&serviceProvided%5B3%5D.pages=--&serviceProvided%5B4%5D.id=DOCREVIEW&serviceProvided%5B4%5D.desc=Document+Review&serviceProvided%5B4%5D.duration=5&serviceProvided%5B4%5D.pages=6
To match this parameter I am using custom class as
Class MyDto {
private Long caseId;
private List<ServiceProvided> serviceProvided;
//getter and setter
}
Class ServiceProvided {
private String id;
private String desc;
private Long duration;
private Long pages;
//getter and setter
}
I have controller as
#RequestMapping(value = "/cases/resolveClaim.htm", method = RequestMethod.POST)
public ModelAndView createClaim(#ModelAttribute("claimInfo") MyDto myDto, BindingResult result) { ... }
I am getting 404 error so I am guessing "serviceProvided" list couldn't match to myDto. So my questions are:
Is this a really a reason I am getting 404 error?
If yes I guess I have to solve with PropertyEditor or Converter? Am I correct?
Is there any example code that I can refer to?
Thanks

Spring4 MVC ModelAndView Constructor

In Spring 4 MVC
In my Controller Class
#Controller
public class TesterController{
#RequestMapping(value = "/test", method = RequestMethod.GET)
public ModelAndView show(){
ModelAndView mav = new ModelAndView("Test");
mav.addObject("Test" , "TestObject");
return mav;
}
}
Can some one explain how is the use of the ModelAndView class in this controller.
What is use of addObject Method
and What is use of having constructor.
Thanks in Advance
Pavan
You can just look at the source as spring is open source.
The model is just a map. So the addObject, is just adding the string "TestObject", with a key of "Test".
A model and view is simply a string of the viewname, along with the associated map/model.
The view resolver will then find the appropriate view file based on viewname, and the map values can be resolved in the view using the key.

Binding a Backbone.js object to a Spring Controller while utilising #InitBinder

I am having trouble getting #InitBinder to work with Backbone.js
#InitBinder
public final void initBinder(final WebDataBinder binder) {
// Customer property editors
binder.registerCustomEditor(MyObjectChild.class, new MyObjectChildEditor());
}
I have a controller like this that accepts the object
#RequestMapping(
produces = MediaType.APPLICATION_JSON_VALUE,
consumes = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.POST)
#ResponseBody
#ResponseStatus(value = HttpStatus.CREATED)
public final String create(#RequestBody final MyObject myobject) {
return "worked";
}
MyObject has a property of type MyObjectChild. I would like to pass the ID values of this object from the frontend to the controller because it does not make sense for the user to have the ability to modify it (they will select it from the drop down list).
I have read that #RequestBody does not work with #InitBinder so I tried #ModelAttribute instead, but I don't know what the #ModelAttribute name would be because it is created on the frontend in javascript using Backbone.js.
How do I get Backbone.js to work with Spring #InitBinder?

Resources