Invalid target for Validator in spring error? - spring

Hi all I am getting the following error whenever I am trying to invoke validator in my spring
Servlet.service() for servlet spring threw exception: java.lang.IllegalStateException: Invalid target for Validator
Please have a look and help me out in this error, previously I user the validation for login page and it is working fine but now its not working.
Here is my code snippet .
Controller
#Controller
public class NewUserRegistration
{
#Autowired
private UserService userService;
#Autowired
private NewUserValidator newUserValidator;
#InitBinder
public void initBinder(WebDataBinder binder)
{
binder.setValidator(newUserValidator);
}
#RequestMapping(value="/newUserAdd", method=RequestMethod.POST)
public String addUser(#ModelAttribute("user")#Valid User user,BindingResult result, Model model)
{
return "NewUser";
}
}
Validator
#Component
public class NewUserValidator implements Validator
{
#Override
public boolean supports(Class<?> classz)
{
return NewUserRegistration.class.equals(classz);
}
#Override
public void validate(Object obj, Errors error)
{
//Validation login for fields
}
}
JSP Page
<form:form action="newUserAdd" method="POST" modelAttribute="user">
<center>
<table>
<tr><td>User Id:</td><td><input name="userId" type="text" /></td><td><font color="red"><c:out value="${userIdError}" /></font> </td></tr>
<tr><td>Password:</td><td><input name="userPassword" type="password"/></td><td><font color="red"><c:out value="${userPasswordError}" /></font></td></tr>
<tr><td>Confirm Password:</td><td><input name="userConfirmPassword" type="password"/></td><td><font color="red"><c:out value="${userPasswordError}" /></font></td></tr>
<tr><td>Name:</td><td><input name="userName" type="text"/></td><td><font color="red"><c:out value="${userPasswordError}" /></font></td></tr>
<tr><td></td><td><input type="submit" value="Create"/></td></tr>
</table>
</center>
</form:form>

The problem is actually in Validator class you are using NewUserRegistration's object which is wrong because you want to validate your User's object not your NewUserRegistration's object.
#Override
public boolean supports(Class<?> classz)
{
return NewUserRegistration.class.equals(classz);
}
which should be
#Override
public boolean supports(Class<?> classz)
{
return User.class.equals(classz);
}

Related

Error 403 when I submit the register form in spring boot project

I am learning spring boot and write a register form, but when I run it in idea and submit the form, the browser occurs
There was an unexpected error (type=Forbidden, status=403).
Forbidden
I create the project by using spring initializr in idea, choose web+jpa+h2+thymeleaf.
I defined an Entity called Worker and set error messages in ValidationMessages.properties, here is the Worker entity
#Entity
public class Worker implements UserDetails {
private static final long serialversionUID = 1L;
#Id
#NotNull
#Size(min = 5, max = 16, message = "{username.size}")
private String username;
#NotNull
#Size(min = 2, max = 30, message = "{firstName.size}")
private String firstname;
#NotNull
#Size(min = 2, max = 30, message = "{lastName.size")
private String lastname;
#NotNull
#Size(min = 5, max = 25,message = "{password.size}")
private String password;
#NotNull
#Size(min = 2, max = 30, message = "{profession,size}")
private String profession;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getProfession() {
return profession;
}
public void setProfession(String profession) {
this.profession = profession;
}
//UserDetails methods
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority("WORKER"));
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
and WorkersRepository
public interface WorkersRepository extends JpaRepository<Worker, String> {
Worker findByUsername(String username);
}
I have added spring security, and wrote the config:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private WorkersRepository workersRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/submit").access("hasRole('WORKER')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.rememberMe()
.tokenValiditySeconds(4838400)
.key("workerKey");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return workersRepository.findByUsername(username);
}
});
}
}
If the register input occurs error, the controller returns the registerForm.html to ask user to input again correctly. If the register has no error, the controller redirects to "/", a simple welcome.html. But whether the input is correct or not, I always get the error 403. When I inputhttp://localhost:8080/, I can get the welcome.html,which is a simple page with "welcome!" words. My controller is
private WorkersRepository workersRepository;
#Autowired
public WorkingHoursController(
WorkersRepository workersRepository) {
this.workersRepository = workersRepository;
}
#RequestMapping(method = RequestMethod.GET)
public String welcomePage() {
return "welcome";
}
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String showRegistrationForm(Model model) {
model.addAttribute(new Worker());
return "registerForm";
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public String registrationProcessing(#Valid Worker worker, Errors errors, RedirectAttributes model) {
if(errors.hasErrors()) {
return "registerForm";
}
workersRepository.save(worker);
model.addAttribute("username", worker.getUsername());
model.addFlashAttribute("worker", worker);
return "redirect:/";
}
...
I wrote the registerForm.html using thymeleaf and add error validations. My registerForm.html is
<form class="form-signin" method="post" th:object="${worker}">
<div class="errors" th:if="${#fields.hasErrors('*')}">
<ul>
<li th:each="err : ${#fields.errors('*')}"
th:text="${err}">Input is in correct.</li>
</ul>
</div>
<img class="mb-4" src="https://getbootstrap.com/assets/brand/bootstrap-solid.svg" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal">Please register</h1>
<!-- input username -->
<label for="inputUsername" th:class="${#fields.hasErrors('username')}? 'error'">Username</label>
<input type="text" id="inputUsername" th:field="*{username}" th:class="${#fields.hasErrors('username')}? 'error form-control':'form-control'" placeholder="Username">
...
<!-- input password -->
<label for="inputPassword" th:class="${#fields.hasErrors('password')}? 'error'">Password</label>
<input type="password" id="inputPassword" th:field="*{password}" th:class="${#fields.hasErrors('password')}? 'error form-control':'form-control'" placeholder="Password">
<div class="checkbox mb-3">
<label>
<input type="checkbox" id="remember-me" name="remember-me"> Remember me
</label>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
Before I add validations in thymeleaf and add spring security, everything seems to work properly.
You did not put any action inside form tag. Perhaps that's why you are getting error. Put action inside form tag like this one
<form class="form-signin" action="#" th:action="#{/register}" method="post" th:object="${worker}">
Please check once whether role should be "WORKER" or "ROLE_WORKER" according to your Spring Security JAR version. Also disable the CSRF in your application, and set global CORS config to accept all requests.
add the following to your config class
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
}
This is security stuff, it prevents hackers from using your open account credentials without your permission i.e. you get an email that say click here (that click takes your user info to another site without your consent). only use the above for dev.
For pro; add a csrf token as a hidden input on your form. this token is in your spring server.
<input type="hidden" name="_csrf" th:value="${_csrf.token}"/>

How to solve "Failed to convert property value[...]"

So I have a form with a dropdown in Spring MVC:
<form:form modelAttribute="user" action="registerVerify" method="post">
<!-- Other fields-->
<spring:message code="register.country" />
<form:select path="country" items="${countryList}" />
<br/>
</form:form>
Which is populated by this controller:
#Controller
public class RegisterController {
#RequestMapping(value = "registerForm", method = RequestMethod.GET)
public String register(#ModelAttribute("user") User user, Model model) {
model.addAttribute("user", new User());
model.addAttribute("countryList", cDao.getCountryMap());
model.addAttribute("companyList", cpDao.getCompanyMap());
return "login/registerForm";
}
#RequestMapping(value = "registerVerify", method = RequestMethod.POST)
public String makeRegistration(#ModelAttribute("user") #Valid User user, BindingResult result,
RedirectAttributes redirectAttributes, Model model) {
if (result.hasErrors()) {
System.out.println(result.getFieldError().getDefaultMessage());
model.addAttribute("org.springframework.validation.BindingResult.user", result);
return "redirect:registerForm";
}
if (dao.add(user)) {
redirectAttributes.addFlashAttribute("user", user);
return "redirect:login";
} else {
return "redirect:registerForm";
}
}
// Service classes bellow
I've made some converters
package br.com.sirious.energyquality.converters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import br.com.sirious.energyquality.dao.CompanyDao;
import br.com.sirious.energyquality.models.Company;
public class IdToCompanyConverter implements Converter<String, Company>{
#Autowired
CompanyDao dao;
#Override
public Company convert(String id) {
return dao.getCompanyByID(Integer.parseInt(id));
}
}
And I've set My WebMVCConfig (and WebApplicationInitializer, and spring-context...)
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addFormatters(FormatterRegistry registry){
registry.addConverter(new IdToCompanyConverter());
}
}
But I still get "Failed to convert property value of type [java.lang.String] to required type [br.com.sirious.energyquality.models.Company] for property 'Company'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [br.com.sirious.energyquality.models.Company] for property 'Company': no matching editors or conversion strategy found"
I've checked on many similar posts but none solved the problem. Can someone diagnose what is happening?
Try using the following ConverterRegistry method:
<S,T> void addConverter(Class<S> sourceType,
Class<T> targetType,
Converter<? super S,? extends T> converter)
Which will result in:
public void addFormatters(FormatterRegistry registry){
registry.addConverter(String.class, Company.class, new IdToCompanyConverter());
}
The problem lies on the path of the form:select tag:
<spring:message code="register.country" />
<form:select path="country" items="${countryList}" />
In order to map to the object that is used as reference, the path has to be the id of the object, so path will be: Country.id.
The idea is similar to what is said here: Spring form binding how to do it ? Cannot convert value of type [java.lang.String] to required type
Also, xuesheng has added some interesting information about registering the converter without using web.xml.

Spring Security Custom login using Java Config Based

I am using Spring Security Java based config. But unable to call process action when user submits login form. Here are my config and java file.
please let me know where I am doing something wrong.
Thanks in advance.
1) Spring security Java Config class
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Bean
public AuthenticationManager authenticationManager() throws Exception{
AuthenticationManager authenticationManager = new ProviderManager(
Arrays.asList(authenticationProvider()));
return authenticationManager;
}
#Bean
public AuthenticationProvider authenticationProvider() throws Exception {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userService);
authenticationProvider.afterPropertiesSet();
return authenticationProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").permitAll()
.antMatchers("/process/success").authenticated()
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/")
.failureUrl("/?auth=fail")
.loginProcessingUrl("/process")
.and().logout().logoutUrl("/logout")
.invalidateHttpSession(true).deleteCookies("JSESSIONID")
.permitAll();
}
}
2) Jsp login Page.
<form name="f" action="./process" method="post">
<fieldset>
<legend>Please Login</legend>
<c:if test="${'fail' eq param.auth}">
<div style="color: red">
Login Failed!!!<br /> Reason :
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>
</c:if>
<c:if test="${'succ' eq param.out}">
<div style="color: blue">
<h2>You have been successfully logged out.</h2>
${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
</div>
</c:if>
<div class="alert alert-success">${param.logout}</div>
<label for="username">Username</label> <input type="text"id="username" name="username" /> <label for="password">Password</label>
<input type="password" id="password" name="password" />
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<div class="form-actions">
<button type="submit" class="btn">Log in</button>
</div>
</fieldset>
</form>
3) Here is Home Controller
#Controller
public class HomeController {
#Autowired
AuthenticationManager authenticationManager;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
System.out.println("index.....");
return "index";
}
#RequestMapping(value = "/process", method = RequestMethod.POST)
public String process(#PathVariable("username") String userName,
#PathVariable("password") String password,
HttpServletRequest request, RedirectAttributes redirectAttr) {
try {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userName, password);
Authentication authenticate = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticate);
} catch (AuthenticationException e) {
System.out.println(e.getMessage());
}
System.out.println("login....." + request.getSession(false));
return "redirect:/process/success";
}
#RequestMapping(value = "/process/success", method = RequestMethod.GET)
public String success() {
System.out.println("success.....");
return "success";
}
#RequestMapping(value = "/logout", method = RequestMethod.GET)
public String logout(HttpServletRequest request) {
System.out.println("logout....." + request.getSession(false)+ " is new " + request.getSession(false).isNew());
request.getSession(false).invalidate();
return "index";
}
}
The problem is that Spring Security uses filters, and the request for normally intercepted and processed by the UsernamePasswordAuthenticationFilter. So it cannot reach your controller.
Spring Security uses a filter to process login for you and you should not even think to use a controller for that. You should read (again) the reference manual and start with a tutorial.

Not showing error message in form(jsp)

I have a problem with showing error message in my form(jsp form).
I create a validator, and want to see errors (if exists) in my form, but the errors not showing, what's problem?
Part of form
<form:form method="POST" action="/regStoreSuccessful" commandName="storeForm">
<table>
<tr>
<td><form:label path="name">Store name</form:label></td>
<td><form:input path="name" /></td>
<td><form:errors path="name" cssclass="error"/></td>
</tr>
Validator
public class StoreValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Store.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.empty", "Name field is empty");
}
}
Controller
#Autowired
private StoreValidator storeValidator;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(storeValidator);
}
//get method
#RequestMapping(value = "/regStore", method = RequestMethod.GET)
public ModelAndView addStore() throws SQLException {
ModelAndView modelAndView = new ModelAndView("Store/regStore", "storeForm", new Store());
}
//post method
#RequestMapping(value = "/regStoreSuccessful", method = RequestMethod.POST)
public ModelAndView addStorePost(#Valid #ModelAttribute("storeForm") Store storeForm, BindingResult bindingResult, Principal principal) throws SQLException {
ModelAndView modelAndView = new ModelAndView("redirect:body");
if(bindingResult.hasErrors()) {
modelAndView.addObject("errors", bindingResult.getAllErrors());
return new ModelAndView("redirect:regStore");
}
storeService.addStore(storeForm);
return modelAndView;
}
The model attributes won't be available after redirect, you should use RedirectAttributes redirectAttrs and store errors as flash attributes, that way attributes will be available after the redirect and removed immediately after used, so change your method to
//post method
#RequestMapping(value = "/regStoreSuccessful", method = RequestMethod.POST)
public ModelAndView addStorePost(#Valid #ModelAttribute("storeForm") Store storeForm, BindingResult bindingResult, Principal principal, , RedirectAttributes redirectAttrs) throws SQLException {
ModelAndView modelAndView = new ModelAndView("redirect:body");
if(bindingResult.hasErrors()) {
redirectAttrs.addFlashAttribute("errors", bindingResult.getAllErrors());
return new ModelAndView("redirect:regStore");
}
storeService.addStore(storeForm);
return modelAndView;
}

Thymeleaf th:href invoking both a post and get

Following this guide I have configured a controller to handle the display and validation of a very simple form. I am using a Thymeleaf th:href to link to the form.
<a th:href="#{/event/create}">Create Event</a>
The problem is that both the showForm() and checkEventForm() are being invoked one after the other upon clicking the link. The result is a quick redirect away from the form back to / since checkEventForm() is also processed.
Are both GET and POST meant to be processed??
The Link :
<div id="sidebar">
<ol>
<li><a th:href="#{/}">Join Event</a></li>
<li><a th:href="#{/event/create}">Create Event</a></li>
</ol>
</div>
MvcConfig :
#Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/home").setViewName("home");
registry.addViewController("/").setViewName("home");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
}
}
CreateEventController :
#Controller
public class CreateEventController {
#RequestMapping(value="/event/create", method=RequestMethod.GET)
public String showForm(CreateEvent event) {
return "createEvent";
}
#RequestMapping(value="/event/create", method=RequestMethod.POST)
public String checkEventForm(#Valid CreateEvent event, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return "createEvent";
}
return "redirect:/";
}
}
After some digging it turned out to be nothing with my program at all! It was in fact a chrome extension (lastpass) that had an auto-saved password for localhost which was attempting an automatic login and submitting the form! Talk about driving yourself nuts!

Resources