SpringMVC UserDetailsService login - spring

I have this class:
#Service
public class AppUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Optional<User> optionalUser = userRepository.findByEmailAndStatus(email, UserStatusEnum.ACTIVE);
User user = optionalUser.orElseThrow(() -> new UsernameNotFoundException("Usuário e/ou senha incorretos"));
return new UserSystem(user, getPermissoes(user));
}
private Collection<? extends GrantedAuthority> getPermissoes(User user) {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
user.getPermitionList().forEach(p -> authorities.add(new SimpleGrantedAuthority(p.getCode().getDescription().toUpperCase())));
return authorities;
}
}
My html form:
<form name="login" method="POST" th:action="#{/login}">
<div class="form-group">
<input type="email" class="form-control input-lg" id="email" placeholder="Email" name="email"
required="required"/>
</div>
<div class="form-group">
<input type="password" class="form-control input-lg" id="password" placeholder="Senha" name="password"
required="required"/>
</div>
<div class="btn-group btn-group-justified" role="group">
<div class="btn-group" role="group">
<button type="button" class="btn btn-danger" onclick="showUserForm()">Cadastar</button>
</div>
<div class="btn-group" role="group">
<button type="submit" class="btn btn-default">Entrar</button>
</div>
</div>
<button type="button" class="btn btn-link wimoo-link">Esqueceu sua senha?</button>
</form>
My login Bean:
#GetMapping("/login")
public ModelAndView login(#AuthenticationPrincipal User user) {
ModelAndView mv = new ModelAndView("login");
mv.addObject(new User());
if (user != null) {
mv = new ModelAndView("masterdata/home");
return mv;
}
return mv;
}
The problem is, the User in my method 'public ModelAndView login(#AuthenticationPrincipal User user)' is always null. The method loadUserByUsername is ok, but in my bean the user is always null.

You are missing a few things. You are not binding your form details to your user in the login method. In order to do the binding do this:
On your form you have:
<div class="form-group">
<input type="email" class="form-control input-lg" id="email" placeholder="Email" name="email" required="required"/>
</div>
if you want to bind this email from your form to the email field on your User object, then if on the User class you have:
public class User{
private String myEmailAddress;
}
then on your input tag, you should add a name attribute that equals to myEmailAddress in order to bind that value from your form to your User object on your login method.
<div class="form-group">
<input type="email" name="myEmailAddress" class="form-control input-lg" id="email" placeholder="Email" required="required"/>
</div>
All you need to do now is to add the #ModelAttribute annotation to your login method in order to actually do this binding. Like this:
#GetMapping("/login")
public ModelAndView login(#AuthenticationPrincipal #ModelAttribute User user) {
ModelAndView mv = new ModelAndView("login");
mv.addObject(new User());
if (user != null) {
mv = new ModelAndView("masterdata/home");
return mv;
}
return mv;
}

Related

Failed to load resource 405

I'm a bit at a loss here.
I have a thymeleaf page and a spring-boot backend that takes in a user object, getting the object to the page is fine, my problem comes in when I'm trying to get it to the back end to do stuff with it.
I keep getting the following
2021-09-15 09:21:07.834 WARN 3624 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
and on the browser
Failed to load resource: the server responded with a status of 405 ()
for my controller I have the following
#Controller("/user")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private ModelMapper modelMapper;
#RequestMapping(value = "/add")
public String addUser(#ModelAttribute("user") final UserDto userDto) {
//do stuff
//userService.save(modelMapper.map(userDto, User.class));
return "/user";
}
}
as for my thymeleaf page
<form th:action="#{/user/add}" th:object="${user}" method="post">
<label for="fullName">Full Name</label>
<input id="fullName" class="form-control form-group" type="text" th:field="*{fullName}">
<label for="email">Email</label>
<input id="email" class="form-control form-group" type="email" th:field="*{email}">
<label for="password">Password</label>
<input id="password" class="form-control form-group" type="password" th:field="*{password}">
<p>
<button class="form-group form-control btn btn-primary" type="submit" value="Submit">Submit</button>
</p>
</form>
What am I missing here?
I did try to mess around the #GetMapping, #PostMapping, #RequestMapping(method = GET), #RequestMapping(method = POST), #RequestMapping(method = {GET, POST})
I also tried <form ... th:method="post"> and <form ... th:method="get">
But none of these seems to work.
You add global /user in #Controller. this annotation is used to implement Web Application not for path and it is better to give global path in application.properties like below code. Inside addUser() method you want to return with page name like return "user" if go to the url then put return "redirect:/user"
Here down is modified code:
application.properties
server.servlet.contextPath=/user/
Controller
#Controller
public class UserController {
#Autowired
private UserService userService;
#Autowired
private ModelMapper modelMapper;
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addUser(#ModelAttribute("user") final UserDto userDto) {
//do stuff
//userService.save(modelMapper.map(userDto, User.class));
return "pagename"; // enter page name where you wanna go not url
}
}
Template
<form th:action="#{/add}" th:object="${user}" method="post">
<label for="fullName">Full Name</label>
<input id="fullName" class="form-control form-group" type="text" th:field="*{fullName}">
<label for="email">Email</label>
<input id="email" class="form-control form-group" type="email" th:field="*{email}">
<label for="password">Password</label>
<input id="password" class="form-control form-group" type="password" th:field="*{password}">
<p>
<button class="form-group form-control btn btn-primary" type="submit" value="Submit">Submit</button>
</p>
</form>

Thymleaf controller request mapping binging issue

I am new to thymleaf and at first i used simple requestmapping and thymleaf th:action and th:object to bind controller methods. But after adding class level requestmapping i cannot view my html.
below is my controller class code where i redirect to login page.
#Controller
#RequestMapping("/project")
public class MyController {
#RequestMapping(value = {"/"}, method = RequestMethod.GET)
public String login(Model model) {
model.addAttribute("mylogin", new Credentials());
return "login";
}
}
below is my html page.
<form class="user" th:action="#{/project/login}" th:object="${mylogin}" method="POST">
<div class="form-group">
<input type="email" id="user_name" name="username"
class="form-control form-control-user"
placeholder="Enter Email Address..." />
</div>
<div class="form-group">
<input type="password" id="password" name="password"
class="form-control form-control-user"
placeholder="Password" />
</div>
<button class="btn btn-primary btn-user btn-block"
name="Submit" value="Login" type="Submit" th:text="Login"></button>
</form>
after adding #RequestMapping("/project") in class i cannot fetch html. If i remove this #RequestMapping("/project") and change th:action="#{/project/login}" to th:action="#{/login}" my code works.
What can be the problem for such issue?
Change This Request mapping this way /login with class level requestmapping and try again:
#RequestMapping(value = {"/login"}, method = RequestMethod.GET)
public String login(Model model) {
model.addAttribute("mylogin", new Credentials());
return "login";
}
OR
#GetMapping("/login")
public String login(Model model) {
model.addAttribute("mylogin", new Credentials());
return "login";
}

form:error not showing error

When validating object controller just refreshes the page, instead showing errors via form:errors, can you tell me where is the problem. I guest it needs to get error from binding result and insert it into the page, instead controller is just refreshing page
Controller :
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/create", method = RequestMethod.POST)
public String createProduct(MultipartFile image, Model model,#ModelAttribute #Valid ProductDto product, BindingResult bindingResult) throws IOException {
productValidator.validate(product,bindingResult);
if (bindingResult.hasErrors()){
return "admin/create";
} else {
return "index";
}
}
#PreAuthorize("hasRole('ROLE_ADMIN')")
#RequestMapping(value = "/create", method = RequestMethod.GET)
public String createProduct(Model model) {
model.addAttribute("product", new ProductDto());
model.addAttribute("categories", categoryService.findAll());
return "admin/create";
}
Validator :
#Override
public boolean supports(Class<?> aClass) {
return ProductDto.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
ProductDto product = (ProductDto) o;
if (product.getTitle().isEmpty() || product.getTitle() == null) {
errors.rejectValue("title", "product.title", "Product title cant be empty");
}
if (product.getDescription().isEmpty() || product.getDescription() == null) {
errors.rejectValue("description", "product.description", "Product description cant be empty");
}
if (product.getPrice() == null || product.getPrice()<=0) {
errors.rejectValue("price", "product.price", "Product price is not valid");
}
if (product.getCategoryId()==null) {
errors.rejectValue("category", "product.category", "Product category is not valid");
}
}
jstl page :
<spring:url value="/product/create" var="formUrl"/>
<form:form modelAttribute="product" action="${formUrl }" method="post" enctype="multipart/form-data">
<div class="form-group">
<label>Title</label>
<form:input id="title"
cssClass="form-control" path="title"/>
<form:errors path="title"/>
</div>
<div class="form-group">
<label>Description</label>
<form:textarea id="description" rows="10"
cssClass="form-control" path="description"/>
</div>
<div class="form-group">
<label>Price</label>
<form:input id="price" type="number"
cssClass="form-control" path="price"/>
<form:errors path="description"/>
</div>
<div class="form-group">
<label for="sel1">Select category</label>
<form:select id="sel1" cssClass="form-control" path="categoryId">
<form:options items="${categories}" itemValue="id" itemLabel="title"/>
</form:select>
</div>
<label class="btn btn-default btn-file">
Image <input type="file" multiple accept='image/*' ng-file-select="onFileSelect($files)" name="image" style="display: block;">
</label>
<br><br>
<div class="text-center">
<button type="submit" class="btn btn-lg btn-success text-center"><span
class="fa fa-check"></span> Submit
</button>
<a href="/" class="btn btn-danger btn-lg text-center"><span
class="fa fa-times"></span> Cancel
</a>
</div>
</form:form>
I think your product model attribute has different name in your POST-handling method.
According to documentation:
The default model attribute name is inferred from the declared
attribute type (i.e. the method parameter type or method return type),
based on the non-qualified class name: e.g. "orderAddress" for class
"mypackage.OrderAddress", or "orderAddressList" for
"List<mypackage.OrderAddress>".
So your ProductDto product attribute will have name productDto in resulting model. But you referring the product in your template.
Try to explicitly set the name attribute of #ModelAttribute annotation
public String createProduct(MultipartFile image, Model model, #ModelAttribute(name = "product") #Valid ProductDto product, BindingResult bindingResult)
And to make sure that bidning and validation actually works, try to log it:
if (bindingResult.hasErrors()) {
System.err.println("Form has errors!");
}

Thymeleaf registration page - Error during execution of processor 'org.thymeleaf.spring4.processor.attr.SpringInputGeneralFieldAttrProcessor'

I'm making a registration page for a website. I understand that in order for a new User to be created, an id is required, so we have the field:
<input type="hidden" th:field="{*id} />
However, when I go to the page, I get the error I mentioned in this post's title.
Here is the form in question:
<form th:action="#{/users/register}" th:object="${user}" class="form-signin" method="POST">
<h2 class="form-signin-heading">Register</h2>
<input type="hidden" th:field="*{id}" />
<label for="inputUsername" class="sr-only">Username*</label>
<input type="text" th:field="*{username}" name="username" id="inputUsername" class="form-control" placeholder="Username" required="required" autofocus="autofocus" />
<label for="inputEmail" class="sr-only">Email Address*</label>
<input type="text" th:field="*{email}" name="email" id="inputEmail" class="form-control" placeholder="Email address" required="required" autofocus="autofocus" />
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" th:field="*{password}" name="password" id="inputPassword" class="form-control" placeholder="Password" required="required" />
<label for="inputConfirmPassword" class="sr-only">Confirm Password</label>
<input type="password" th:field="${confirmPassword}" name="confirmPassword" id="inputConfirmPassword" class="form-control" placeholder="Confirm password" required="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
</form>
Here is my UserController:
#RequestMapping("/register")
public String registerAction(Model model) {
model.addAttribute("user", new User());
model.addAttribute("confirmPassword", "");
return "views/users/register";
}
#RequestMapping(value="/register", method = RequestMethod.POST)
public String doRegister(User user) {
User savedUser = userService.save(user);
return "redirect:/"; //redirect to homepage
}
And the first part of the User entity:
#Entity
#Table(name = "users")
public class User {
// Default constructor require by JPA
public User() {}
#Column(name = "id")
#Id #GeneratedValue
private Long id;
public void setId(long id) {
this.id = id;
}
public long getId() {
return id;
}
From what I can see, there's nothing wrong here so I'm stuck.
I'm following this example: https://github.com/cfaddict/spring-boot-intro
Any ideas?
The problem is the way you have declared your id property. The field uses a reference type Long which is null. The getter uses a primitive long. When Spring accesses the id field it tries to unbox a null value causing an error. Change your domain class to be
#Entity
#Table(name = "users")
public class User {
// Default constructor required by JPA
public User() {}
#Id
#Column(name = "id")
#GeneratedValue
private Long id;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
}
I don't know if you have a class like this
#Controller
#RequestMapping("/users")
public class MyController{
#RequestMapping("/register")
public String registerAction(Model model) {
model.addAttribute("user", new User());
model.addAttribute("confirmPassword", "");
return "views/users/register";
}
#RequestMapping(value="/register", method = RequestMethod.POST)
public String doRegister(User user) {
User savedUser = userService.save(user);
return "redirect:/"; //redirect to homepage
}
}
becouse if you don't have the #RequestMapping("/users") this is a problem becous if you dont have this annotation in you class the correct actione in the thymeleaf templace should be "#{/register}" other wise yon don't have the endpoint pubblished in other words with the methods that you posted you should have a template like this:
<form th:action="#{/register}" th:object="${user}" class="form-signin" method="POST">
<h2 class="form-signin-heading">Register</h2>
<input type="hidden" th:field="*{id}" />
.... should be as you written
<input type="password" th:field="*{confirmPassword}" id="inputConfirmPassword" class="form-control" placeholder="Confirm password" required="required" />
<button class="btn btn-lg btn-primary btn-block" type="submit">Register</button>
</form>
reading beter the your html you probably should have th:field="*{confirmPassword}" and not th:field="${confirmPassword}".
another think that in my opinion don't works is that you repeate name attribute. The my advice is don't repeate and let to thymeleaf the work of build the correct attribute for databinding.

Spring validator - The request sent by the client was syntactically incorrect

My friend helped me up set my first spring/hibernate project and now I am trying to implement a custom validator. I already have a RegistrationForm model that uses anotations to validate. However, now I need to implement a password validator - to check if the password and confirmPassword are equal.
Problem: when I POST, if the passwords match, the data is saved. If they dont match, then at the validators following line, I get the error from the title.
errors.rejectValue("confirmPassword", "valid.passwordConfDiff");
The request sent by the client was syntactically incorrect.
Tutorial I followed:
http://examples.javacodegeeks.com/enterprise-java/spring/mvc/spring-mvc-password-example/
This is what I have:
Controller:
#Controller
#RequestMapping("/")
//#SessionAttributes(value = {"registerForm"})
#ComponentScan("ba.codecentric.movienight")
public class FrontController {
#Autowired
private UserService userService;
#Autowired
#Qualifier("passwordValidator")
private PasswordValidator validator;
#InitBinder("password")
private void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
#RequestMapping(method = RequestMethod.GET)
public String initIndex(Model model){
model.addAttribute("registerForm", new RegisterForm());
Password password = new Password();
model.addAttribute("password", password);
return "index";
}
#RequestMapping(value="/register", method=RequestMethod.POST)
public String addNewUser(#Valid #ModelAttribute("registerForm") RegisterForm registerForm,
BindingResult result, Model model, HttpServletRequest request, #Validated Password password){
if(result.hasErrors()){
model.addAttribute("error", true);
model.addAttribute("userIsRegistering", "<script>$('#loginbox').hide(); $('#signupbox').show();</script>");
System.out.println("ERROR");
return "index";
}
User user = new User();
user.setUserName(registerForm.getUserName());
user.setPassword(registerForm.getPassword());
userService.addNewUser(user);
return "index";
}
}
Password validator:
public class PasswordValidator implements Validator{
public boolean supports(Class<?> paramClass) {
return Password.class.equals(paramClass);
}
public void validate(Object obj, Errors errors) {
Password password = (Password) obj;
if (!password.getPassword().equals(password.getConfirmPassword())) {
errors.rejectValue("confirmPassword", "valid.passwordConfDiff");
}
}
}
RegistrationForm:
public class RegisterForm {
#NotBlank
#NotEmpty
private String userName;
#NotBlank
#NotEmpty
#Size(min=7,max=16,message="Password range error")
private String password;
#NotBlank
#NotEmpty
private String confirmPassword;
//getters setters
}
Registration form jsp:
<form:form id="signupform" class="form-horizontal" role="form" commandName="registerForm" action="${pageContext.request.contextPath}/register" method="post">
<form:errors path="*" cssClass="errorblock" element="div" class="alert alert-danger" role="alert" />
<div class="form-group">
<label for="userName" class="col-md-3 control-label">User Name</label>
<div class="col-md-9">
<form:input type="text" path="userName" class="form-control" name="userName" placeholder="User Name" />
</div>
</div>
<form:errors path="userName" element="div" class="alert alert-danger" role="alert" />
<div class="form-group">
<label for="password" class="col-md-3 control-label">Password</label>
<div class="col-md-9">
<form:password path="password" class="form-control" name="password" placeholder="Password"/>
</div>
</div>
<form:errors path="password" element="div" class="alert alert-danger" role="alert" />
<div class="form-group">
<label for="confirmPassword" class="col-md-3 control-label">Confirm Password</label>
<div class="col-md-9">
<form:password path="confirmPassword" class="form-control" name="confirmPassword" placeholder="Confirm Password"/>
</div>
</div>
<form:errors path="confirmPassword" class="alert alert-danger" role="alert" />
<div class="form-group">
<!-- Button -->
<div class="col-md-offset-3 col-md-9">
<form:button id="btn-signup" type="submit" class="btn btn-info"><i class="icon-hand-right"></i> &nbsp Sign Up</form:button>
</div>
</div>
</form:form>
I have also faced the same problem.
#RequestMapping(method = RequestMethod.POST)
public String processForm(
#Valid #ModelAttribute("student") Student student, Model model,
BindingResult result) {
if (result.hasErrors()) {
return "registration";
}
return "success";
}
When the object student has invalid attribute(s), then this code gives the error: The request sent by the client was syntactically incorrect.
However, if the order of arguments student and model is changed, then this method works as expected.
E.g.,
public String processForm(Model model, #Valid #ModelAttribute("student") Student student, BindingResult result)
and
public String processForm( #Valid #ModelAttribute("student") Student student, BindingResult result, Model model)
both work as expected.
Please, let me know what is the science behind this.

Resources