Sending HttpSession from one controller to another in spring mvc - java

I am redirecting my request from one controller to another, but while doing that I am loosing my session attributes. But I want to use the session only so that I can verify if user is logged in or not. Whole condition is explained below:
Login.jsp
This is the page in which user gives its username and password and the request goes to "LoginController" .
<%# page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login</title>
</head>
<body>
${loginError }
<h1>Login Details</h1>
<form action="${pageContext.request.contextPath }/login" method="post">
<table>
<tr>
<td>Username</td>
<td><input type="text" name="userId" ></td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" name="password" ></td>
</tr>
<tr>
<td><input type="submit" name="Submit" value="Submit"></td>
</tr>
</table>
</form>
</body>
</html>
LoginController.java
As the action was "login" using post method so the request maps to "verifyLogin()" method.
I have checked for the customer name it is coming after calling "loginCustomer()" method of "CustomerService" class.
But when I set it in session attributes and redirect it to url which match up to "HomeController" method I am losing that session attributes their.
#Controller
#RequestMapping(value="/login")
public class LoginController {
#Autowired
private CustomerService customerService;
#RequestMapping(method=RequestMethod.GET)
public String showLoginForm(){
return "login";
}
#RequestMapping(method=RequestMethod.POST)
public String verifyLogin(#RequestParam String userId, #RequestParam String password, HttpSession session, Model model){
Customer customer = customerService.loginCustomer(userId, password);
if(customer==null){
model.addAttribute("loginError", "Username or password is not correct");
return "login";
}
session.addAttribute("loggedInUser", customer);
return "redirect:/";
}
#RequestMapping(value="logout", method=RequestMethod.GET)
public String logoutUser(HttpSession session){
session.removeAttribute("loggedInUser");
return "login";
}
#ExceptionHandler(Exception.class)
public ModelAndView handleException(){
ModelAndView mav = new ModelAndView();
mav.addObject("loginError", "Usrname or password is not correct");
mav.setViewName("login");
return mav;
}
}
HomeController.java
The request mapped to "home()" method. This method is also called when application loads up, and when user logged in correctly then also it is called.
Here in this method I am loosing my session attribute, but I want it in the page were this method maps i.e. home.jsp the return value of this method.
As session attribute is not available in this method so its not available in the home.jsp page also.
Please help.
#Controller
public class HomeController {
#Autowired
private ProductService productService;
#Autowired
private CategoryService categoryService;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
List<Product> products = productService.getFeaturedProducts();
List<Category> categories = categoryService.getAllCategories();
model.addAttribute("featuredProduct", products);
model.addAttribute("categories",categories);
return "home";
}
}

I would suggest to go for RedirectAttributes.
redirectAttrs.addFlashAttribute("loggedInUser", customer);
Further reading: -Flash Attribute

My first guess would be that your session Id cookie might get lost between login and next page. (could be because the Web app is configured not to use cookies or the cookie is set for the wrong domain)
To verify check in your browser that:
After successful login the server sets the session cookie for the current host (chrome or Firefox Dev tools can help you here)
And also that your Browser sends the same cookie in the next request for the homepage.
If this does not help please also include the set cookie and the cookie headers in your reply

I've used session with the help plain old HttpServletRequest request and that seemed to serve the purpose for me.
#RequestMapping(method=RequestMethod.POST)
public String verifyLogin(#RequestParam String userId, #RequestParam String password, HttpServletRequest request, Model model){
Customer customer = customerService.loginCustomer(userId, password);
HttpSession session = request.getSession(false);
if (session == null) {
session = request.getSession();
}
if(customer==null){
model.addAttribute("loginError", "Username or password is not correct");
return "login";
}
session.addAttribute("loggedInUser", customer);
return "redirect:/";
}
By this way, you can access the session values through the request object in both your controller and jsp page.

Related

How to create a custom message/error message in Spring controller and display it in template?

I am trying to create a custom error message in my controller and then pass it into the HTML template if the email is already found in the database.
The controller method does work and it will redirect the user to "registration-form" if a duplicate email is used, but the error message is not displayed on the web page as I intend it to be.
I basically want the error message to appear on the screen when the user cannot register with that email.
In HTML form:
<p th:text="*{errorMessage}"></p>
In Controller:
#PostMapping("/process-register")
private String processRegistrationForm(Model model, WebUser webUser) {
String email = webUser.getEmail();
if(webUserRepo.findByEmail(email) == null) {
webUser.setRole("user");
WebUser savedWebuser = webUserRepo.save(webUser);
model.addAttribute("userID", savedWebuser.getUserID());
String userID = String.valueOf(savedWebuser.getUserID());
return "redirect:/welcome/" + userID;
}
else {
String errorMessage = "This email address is already registered, please login to your existing account.";
model.addAttribute(errorMessage);
return "registration-form";
}
}
}
When your original request is not cached (i.e. is a POST, DELETE, PUT, ... operation) you can respond with any MVC flow, simply be sure to not use the path for "weird" things (neither server nor client side).
Then, if the email exists put the message into the model and redirect to the remember password (or login ...).
So, you can prepare your RememberController to be called from others controllers decoupling internal state and logic:
#Controller
#RequestMapping("/remember")
public class RememberController {
#Getter
#Setter
#AllArgsConstructor
public static class RememberModel {
private String email;
}
// this method decouple the RememberController knowledge (i.e. merge, remove, get messages, ... needed by this view)
public String redirect(Model model, String email) {
model.addAttribute("form", new RememberModel(email));
return get(model);
}
#GetMapping
public String get(Model model) {
if (model.getAttribute("form") == null)
model.addAttribute("form", new RememberModel(""));
return "remember";
}
#PostMapping
public String post(#ModelAttribute(name = "form") RememberModel form, Model model) {
model.addAttribute("info", "password sent to " + form.getEmail() + "!");
return "remember";
}
}
Now, your RegisterController can redirect with any required information (i.e. the error message and the user email to not to rewrite twice):
#Controller
#RequestMapping("/register")
public class RegisterController {
#Getter
#Setter
#AllArgsConstructor
public static class RegisterModel {
private String email;
}
#Autowired
protected RememberController rememberController;
#GetMapping
public String get(Model model) {
if(model.getAttribute("form") == null)
model.addAttribute("form", new RegisterModel(""));
return "register";
}
#PostMapping
public String post(#ModelAttribute(name = "form") RegisterModel form, Model model) {
// ... when email exists ...
model.addAttribute("error", "e-mail `" + form.getEmail() + "` exists!");
return rememberController.redirect(model, form.getEmail());
}
}
Then, the user UI works as expected:
GET
POST and server redirecto to remember controller
Then user simply click:
The views are:
<!doctype html>
<html th:attr="lang=en" xmlns:th="http://www.w3.org/1999/xhtml">
<body>
<h1>REGISTER FORM</h1>
<div style="color: red" th:if="${error != null}" th:text="${error}"></div>
<div style="color: green" th:if="${info != null}" th:text="${info}"></div>
<form method="post" th:object="${form}">
<input th:field="*{email}" type="text"/>
<input type="submit" value="register"/>
</form>
</body>
</html>
and
<!doctype html>
<html th:attr="lang=en" xmlns:th="http://www.w3.org/1999/xhtml">
<body>
<h1>REMEMBER FORM</h1>
<div style="color: red" th:if="${error != null}" th:text="${error}"></div>
<div style="color: green" th:if="${info != null}" th:text="${info}"></div>
<form method="post" th:action="#{/remember}" th:object="${form}">
<input th:field="*{email}" type="text"/>
<input type="submit" value="remember"/>
</form>
</body>
</html>
NOTE: for a better user experience, you can change the url when the remember view is redirected on server using window.history.pushState or so.

How can I have a model attribute persist between redirects?

I am trying to write a password reset function for a website. I am running into an issue that I am using a couple of redirects to transition from postmappings to getmappings and they dont seem to be carrying the attributes they need to with them, namely the user object that I am trying to reset the password form, here is an example of one of my mappings:
#PostMapping("/user/forgot")
public String emailCheck (#RequestParam String email, Model model){
User user = userDao.findByEmail(email);
if (user==null){
model.addAttribute("wrongEmail", true);
return "redirect:/user/forgot";
}
else {
model.addAttribute("user", user);
return "redirect:/verifysecurity";
}
}
And here is the template where I then call the user attribute:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head th:replace="fragments/header :: header('Security', '')"></head>
<body>
<div th:replace="fragments/navbar :: navbar"></div>
<h1 th:if="${wrongAnswer}">Answer does not match record on file, please try again</h1>
<h1>Please answer your security question: WHat is your best friends name?</h1>
<form id="passwordForm" th:action="#{/verifysecurity}" th:method="post">
<label for="answer">Answer</label>
<br/>
<input type="text" id="answer" name="answer"/>
<input type="hidden" name="user" id="user" th:value="${user}"/>
<input type="submit" class="btn btn-block btn-primary" value="Request"/>
</form>
</body>
</html>
Then on the next mapping afterwards I get a null pointer exception for the user:
#PostMapping("/verifysecurity")
public String verify (Model model, #RequestParam User user, #RequestParam String answer){
String security = user.getSecurity_question();
if (answer.equals(security)){
model.addAttribute("user", user);
return "redirect:/reset/password";
} else {
model.addAttribute("wrongAnswer", true);
model.addAttribute("user", user);
return "redirect:/verifysecurity";
}
}
How can I fix this, and if model attributes won't work what should I be doing instead?
Use spring RedirectAttributes.addFlashAttribute(), as name suggested it's stored in flashmap which internally uses user session to pass on this data to next redirect, and removes ones data is used.
Example from spring doc:
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handle(Account account, BindingResult result, RedirectAttributes redirectAttrs) {
// Save account ...
redirectAttrs.addFlashAttribute("message", "Account created!");
return "redirect:/accounts/{id}";
}

Spring and german letters in URL issue

I am facing this issue while trying to redirect a user from a login page (Spring) to a url that contains special german letters (ä, ö, ü, ß). This is likely not limited to them, but these are important at the moment.
The workflow looks like this:
User goes to a login page with a redirectUrl request param, for example: http://www.example.com/login.do?redirectUrl=http%3A%2F%2Fwww.example.com%2F%25C3%25B6l%2F, where %25C3%25B6l translates to öl
After logging in successfully, the user should be redirected to http://www.example.com/öl/. However, the actual URL is http://www.example.com/�l/, for some strange reason.
Looking at the URL Redirect trace, seems like (Spring?) encodes the ö to %F6 (Unicode?) instead of %C3%B6 (UTF-8).
The spring container (Tomcat) is all set to have UTF-8 encodings.
I have also tried encoding and decoding the URL to and from ISO-8859-1 and UTF-8, but without luck.
What I have also tried and seemed to work was to manually convert the special letters to their UTF-8 encoding (e.g. string.replace("ö", "%C3%B6");" and pass the url in that form. However, this is ugly and I would prefer not to do that.
Any suggestions on how to properly handle this?
Thanks.
Assumed you are in context of a Spring MVC application, redirects can be implemented using RedirectView or simply with the prefix redirect: as return value of a controller method. Catch it as RequestParam during form initialization and keep it in the session as shown in the following example:
Model:
public class LoginForm implements Serializable {
private String username;
private String password;
private String redirectUrl;
// getters and setters omitted
}
View:
<%# taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<form:form method="POST" modelAttribute="loginForm" acceptCharset="UTF-8" action="login/doLogin">
<table>
<tr>
<td><form:label path="username">User Name</form:label></td>
<td><form:input path="username"/></td>
</tr>
<tr>
<td><form:label path="password">Password</form:label></td>
<td><form:password path="password"/></td>
</tr>
</table>
<div>
<input type="submit" value="Submit"/>
</div>
</form:form>
</body>
</html>
Controller:
#Controller
#RequestMapping("login")
#SessionAttributes(LoginController.FORM_NAME)
public class LoginController {
static final String FORM_NAME = "loginForm";
private static final String DEFAULT_REDIRECT_URL = "https://google.com";
#ModelAttribute(FORM_NAME)
public LoginForm createForm() {
return new LoginForm();
}
#GetMapping
public String showForm(Model model, #ModelAttribute(FORM_NAME) LoginForm loginForm, #RequestParam(required = false) String redirectUrl) {
loginForm.setRedirectUrl(redirectUrl);
model.addAttribute(FORM_NAME, loginForm);
return "login/login-form";
}
#PostMapping("/doLogin")
public String processFormCredentials(LoginForm loginForm) {
// input validation omitted ...
return "redirect:" + (loginForm.getRedirectUrl() != null ? loginForm.getRedirectUrl() : DEFAULT_REDIRECT_URL);
}
}
Using this code, the redirect with umlauts works fine on Tomcat - http://<url>/login?redirectUrl=http%3A%2F%2Fwww.example.com%2F%25C3%25B6l%2F redirects on form submission to http://www.example.com/öl/.

Creating and persisting a new User based on html input. Spring MVC, JPA Hibernate, Thymeleaf

Searched all over the Internet and tryed different solutions, but somehow no one worked in my case.
I have following html template for a view where you should register yourself:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" type="text/css" th:href="#{/css/style.css}" />
<!-- Page title -->
<title> Register</title>
</head>
<body>
<form action="#" th:action="#{/registeruser}" th:object="${user}" method="post">
<input type="text" th:field="*{username}" />
<input type="text" th:field="*{email}" />
<input type="text" th:field="*{password}" />
<input type="submit" />
</form>
</body>
</html>
also here is the controller:
#Controller
public class RegisterController {
#Autowired
private UserService userservice;
#Autowired
private RoleService roleService;
#RequestMapping(value = "/register", method = RequestMethod.GET)
public String regUser(Model model) {
User u = new User();
u.setUsername("username");
u.setPassword("password");
u.setEmail("email");
Set<Role> userRoles = new HashSet<>();
userRoles.add(roleService.findRoleByName("ROLE_USER"));
u.setRoles(userRoles);
System.out.println("test");
model.addAttribute("user", u);
return "register";
// return new ModelAndView("register", "command", new User());
}
#RequestMapping(value = "/registeruser", method = RequestMethod.POST)
public String addUser(#ModelAttribute(value = "user")User user,
ModelMap model) {
model.addAttribute("username", user.getUsername());
model.addAttribute("password", user.getPassword());
model.addAttribute("email", user.getEmail());
System.out.println("user " + user.getUsername() + "got registered, here a list of all users: " );
return "login";
}
}
The console print test works in the regUser() method but somehow the console print in the addUser() method does not work, so I guess it does not get triggered?
Also the View works perfect and I get redirected after submitting to the login page, but this is also the defaultSuccesFulUrl so it probably means nothing.
Kinda frustrated on this one, so it would be really nice if you could help.
Thanks in advance.
Try removing action="#" in your form. This may be stepping on the one that's generated through Thymeleaf.
Aside:
You can use #GetMapping and #PostMapping as shorthand for your
mappings.
You'll want to change it to input type="password"
You can use Model instead of ModelMap
Use #Slf4j and a logging system of your choice
Change your POST method to
#RequestMapping(value = "/registeruser", method = RequestMethod.POST)
public String addUser(User user) {
...
Also is better if in your submit, says is a button
<button type="submit">Submit</button>
And as said bphlilipnyc remove the action="#"

How to display data of objects in JSP

I have stored some user details through a register form into db (hibernate and spring). I want to display the user details of all users in a separate JSP page.Could anyone please tell me how to do that?
Below is my code of controller
#Controller
public class RegisterController {
#Autowired
private UserDao userDao;
#RequestMapping(value = "/registerForm.htm", method = RequestMethod.GET)
public ModelAndView registerPage(ModelMap map) {
User user = new User();
map.addAttribute(user);
return new ModelAndView("registerForm", "command", user);
}
#RequestMapping(value = "/registerProcess.htm", method = RequestMethod.POST)
public ModelAndView registerUser(#ModelAttribute("user") User user, Model model) {
model.addAttribute("userName", user.getUserName());
model.addAttribute("password", user.getPassword());
model.addAttribute("emailId", user.getEmailId());
System.out.println("user is " + user);
System.out.println("userdao is" + userDao);
userDao.saveUser(user);
return new ModelAndView("registerProcess", "user", user);
}
}
code inside userdao
public void saveUser(User user) {
Session session=getSessionFactory().openSession();
Transaction tx;
tx=session.beginTransaction();
session.persist(user);
tx.commit();
}
You should obtain the elements you want to show to user in a GET request. This involves the following steps:
Have a proper URL mapping and view to process the GET.
Obtain the data in the method that will pre process your URL.
Store the data to display to users as request attribute.
Forward to the view (JSP).
In view, display the data from request attributes.
A very simple example based on your current code and assuming the existence of some methods:
#Controller
public class RegisterController {
#Autowired
private UserDao userDao;
#RequestMapping(value="/registerForm.htm",method=RequestMethod.GET)
public ModelAndView registerPage(ModelMap map){
User user=new User();
map.addAttribute(user);
return new ModelAndView("registerForm","command",user);
}
#RequestMapping(value="/registerProcess.htm",method=RequestMethod.POST)
public ModelAndView registerUser(#ModelAttribute("user") User user,Model model){
model.addAttribute("userName", user.getUserName());
model.addAttribute("password", user.getPassword());
model.addAttribute("emailId",user.getEmailId());
System.out.println("user is "+user);
System.out.println("userdao is"+userDao);
userDao.saveUser(user);
return new ModelAndView("registerProcess","user",user);
}
//this is the new method with proper mapping
#RequestMapping(value="/userList.htm", method=RequestMethod.GET)
public ModelAndView registerPage(ModelMap map) {
//this method should retrieve the data for all users
List<User> userList = userDao.getAllUsers();
map.addAttribute("userList", userList);
return new ModelAndView("userList", map);
}
}
Then, in userList.jsp:
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>User List</title>
</head>
<body>
List of users:
<br />
<table>
<c:forEach items="${userList}" var="user">
<tr>
<td>${user.userName}</user>
</tr>
</c:forEach>
</table>
</body>
</html>
Note that this is a very basic example about how to do this. The code can be heavily improved.
More info:
How to pass parameter to jsp:include via c:set? What are the scopes of the variables in JSP?
How to avoid Java code in JSP files?
Write another method to get all the users and then store the list of retrieved users in your model object then use the JSTL forEach tag in your JSP to display the users, you can use this link to see how the data can be displayed on JSP using JSTL forEach loop: JSP Errors in ForEach Loop

Categories