properly use session in spring mvc - java

I use Spring 4.1.1. And I must make service of user session. What's the best way of storing session related data of a user? I read so many way's , but I don't understand which way is proper?
it's example that I need
#Controller
#SessionAttributes("user")
public class PagesController {
#RequestMapping(value="/sign_in", method = RequestMethod.POST)
public String getSignIn(#RequestParam(value="user")String user ,
#RequestParam(value="pass")String password,
Model model) {
UserDAO dao = new UserDao();
if(dao.isUserValid(user,password) && !model.containsAttribute("user")){
User user = new User();
model.addAttribute("user",user);
return USER_PAGE;
}
return LOGIN_PAGE;
}
}

First of all, Session Attribute is not a good option to store your user object. It is spring who decides when to clear a session attribute data. As per spring documentation, spring removes a session attribute when it understands that a 'conversation' is completed. You only use session attribute when you are in a controller scope and the data is temporarily needed to be stored in the session.
As far as user login object goes, the thing you need to do is to use http sesison. When you login/sign in to your application you actually post the login credential to your controller. Once validated, you put the user object (with your necessary info-as less as possible- in to an object and store in to your session). This object will remain as long as it doesn't expire or you clear it when the user trigger logout.
Moreover if you still want to use SessionAttribute to store your user Object. Then there can be further problem when you deploy your application to a clustered environment. Your session will have to be copied to each instance of your server unless you implement sticky session. Copying httpsession is the simplest of task whereas copying the same instance of a sessionAttribute is not.
#RequestMapping(value = "login.html", method = RequestMethod.POST)
public ModelAndView post(#ModelAttribute("login") LoginEntity login, HttpServletRequest req) {
... process the data ...
if passed put it into session:
HttpSession session = req.getSession(true);
UserObject userObject=new UserObject();
userObject.setName(login.getUserName());
...
session.setAttribute("user",userObject);

It is OK that you put your user object in session, and then use it in your project everywhere. However, if you get a lot of users, that means you have many user object in the memory of your server. The memory might run out.
Another way to do it is to put some user information in cookies with some encryption and validation, but just remember not to put too much info in cookies because cookies will be sent every time a request or a response is made. If there to much information to send, it will slow the response time.
And just a reminder, you should call status.setComplete() to clean the attributes inside a session when they are not needed.
Does SessionStatus object.setComplete() clears all the session attributes or just work for the controller in which it is used?
and if you don't know how to use it, you can see the article below
http://vard-lokkur.blogspot.tw/2011/01/spring-mvc-session-attributes-handling.html

Related

Spring #ResponseBody can not use session

I have angular2 app and use Tomcat with spring for getting data. I don't want any page reloads or redirects, all I need is data, so all responses from server have #ResponseBody annotations.
My problem is, that because of this annotation I can not get users session variable. When I log in I create session, store user data in it but can not get it with next call.
#JsonIgnoreProperties(ignoreUnknown = true)
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public String login(HttpServletRequest REQ, #RequestBody String BODY) throws Exception
{
...check if all ok...
REQ.getSession().setAttribute("user", user);
... return user data...
}
Is there any other way I can send my data back to client, together with the data needed, to be able to use session.
Edit:
Problem is not on server side but client. Angular is not sending cookie JSESSIONID at cross domain requests by default.
First check your request/response (for example in Chrome dev tools). Tomcat creates new cookie named JSESSIONID to bind client with server session object, so look for this one in your login method response header. Then make sure you are sending this cookie back to your server in next request. Session creation has nothing to do with Spring or #ResponseBody, its lifetime is managed by container (Tomcat).
Also, if you are making cross domain requests, check this answer.
Anything you put on the session context isn't available to clients, it's only available to the server. see this stack overflow post for a good explanation on how servlets work, and specifically the part on how session state works.
As you can see, it works by adding a session-cookie to the response which contains a session-Id. The server stores the session state under that id in memory, and when a future request comes with that session-id, it makes the session state available again to the future request.
If your next requests do not have access to the session state, it's quite possible that the session-cookie isn't propagated properly. I suggest you check that first. It should be in the response where you log in, and should be posted in further requests to the server.

SessionAttributes when open new browser tabs

I have an Spring-mvc application and in each controller I add a form to SessionAttributes to preserve properties when save, delete or do another get request. Main problem becomes when I try to open some link in another browser tab and try to submit the first one. I tried this solution but when I do a redirect (in controller I only have 1 return for view and the other methods do a redirect) it creates a new conversation and can't find previous one.
I have another question about this triying to use spring-session, question It's here but I don't know if this will work too.
Did you look into Spring's RedirectAttributes? I haven't used it myself but it sounds like it should do what you would like. RedirectAttributes is typically used for GET/redirect/POST patterns and at least one user seems to think passing session attributes this way is bad practice, however they go on to mention there doesn't seem to be a better solution. Anyway, the example shown in the documentation:
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handle(Account account, BindingResult result, RedirectAttributes redirectAttrs) {
if (result.hasErrors()) {
return "accounts/new";
}
// Save account ...
redirectAttrs.addAttribute("id", account.getId()).addFlashAttribute("message", "Account created!");
return "redirect:/accounts/{id}";
}
would add the "message" attribute to a RedirectModel, and if your controller redirects, then whatever method handles the redirect can access that data like so:
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handleRedirect(Model model) {
String message = (String) model.asMap().get("message");
return new ModelAndView();
}
So adding session attributes should be possible in the same way. Another reference here.
EDIT
I was looking through the Spring documentation and they also mention this annotation #SessionAttributes. From the documentation:
The type-level #SessionAttributes annotation declares session attributes used by a specific handler. This will typically list the names of model attributes or types of model attributes which should be transparently stored in the session or some conversational storage, serving as form-backing beans between subsequent requests.
Could this be what you need?
And also a link to documentation on flash attributes.
This is the solution we have come up with, nothing to do with Spring:
On each html form of your application you will have to include a hidden field. Let's name this field CSRF_TOKEN. This field should have a randomly generated value. This value is placed both in the session and the hidden field. The name of the session attribute is SESSION_CSRF_TOKEN
When the form is submitted to the server, you check whether the value in the session (SESSION_CSRF_TOKEN) equals the value sent in the HTTP request parameter CSRF_TOKEN. If not, you show some kind of error message and you stop processing. If they are equal, proceed.
If the user opens a new tab or duplicates a tab, the server will re-render the page and a new CSRF_TOKEN will be generated. So the user will only be able to submit the form from the newly opened tab , and not from the original.
This solution offers an additional bonus: It protects from CSRF attacks.

How to store retrieved object in session and access it afterwords?

I am trying to create a simple login page. I retrieve a User object from my database using hibernate. That part works fine, I'm doing that as follows:
//data from login form
String username = request.getParameter("username").trim();
String password = request.getParameter("password").trim();
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
try {
User currentUser = (User) session.get(User.class, username);
if(password.equals(currentUser.getPassword())) {
response.sendRedirect("index.jsp?page=login&success=true");
} else {
session.getTransaction().rollback();
response.sendRedirect("index.jsp?page=login&success=false");
}
} catch //...
Given the correct credentials, login is successful. If I understand correctly, my code above already stores the User in the session, if the login was successful, so all I have to do is access the session?
However I can't figure out how to access the retrieved User object from the session in other places of my website. After the user is logged in, I want to show user-specific information on my website and for that, I need to check the username and whether the user is logged in at all.
So to sum up: How can I use the retrieved User object in other parts of my website?
I just started learning Java EE and hibernate, so please bear with me.
You can do it using an HttpSession that can be retrieved by the HttpServletRequest object.
HttpSession httpSession = request.getSession();
httpSession.setAttribute("user", user);
Now to check if the user object is present in different parts of your application, you can do the following:
HttpSession httpSession = request.getSession(false);
//False because we do not want it to create a new session if it does not exist.
User user = null;
if(httpSession != null){
user = (User) httpSession.getAttribute("user");
}
if(user!=null){
// Do stuff here
}
To logout a user or in other words, to invalidate the session, you can call the invalidate method.
httpSession.invalidate();
Useful links: HttpServletRequest and HttpSession
HttpSession is different from the Hibernate session. The Hibernate session provides a way for you to query and save persistent entities that are stored in a database. The HttpSession is provided by the servlet container to give a way to
store objects for a given user based on a cookie provided in the user's request.
What you store in the HttpSession should be minimal, partly to save on overhead from nodes in the cluster reconciling their sessions but mostly to make your application less error-prone. Here it could be sufficient to store a user's ID in the session rather than the whole user object. Even if the User object contained roles it would be better to look those up for each request so that any changes get applied immediately. Also by storing only ids you avoid problems with reattaching entities (allowing you to avoid one of the more confusing and troublesome parts of using Hibernate). When something else in your application needs to access the User it can query the Hibernate session (using session.get(id)) passing in the primary key value stored in the HttpSession.
You should use a 1-way hash to store passwords so that will change how you compare passwords.
The application should create a Hibernate SessionFactory once only upon initialization, it is threadsafe and everything in the application should use that one instance.
Rolling back a transaction where all you do is select seems unnecessary.
Typically you access the HttpSession only from the view and the web controller. It looks like you have web controller logic and business logic lumped together, a division of responsibilities between controller and service may be helpful here.
Assuming you are in a Web application and you want something from the User entity, you should propagate the same value/reference to the Web/controller layer (in case you are using an MVC approach); then keep it there since it's the most appropriate place to store something via the HTTP session provided by most frameworks.
RECOMMENDATION(S)
You should not be rolling back a get/select operation?
A SessionFactory should be instantiated once.
The Session you have mentioned here(org.hibernate.Session) is cannot access from the other places of your web site instead you put your User object into a HttpSession .
Here is how you going do this:
HttpSession httpSession = request.getSession();
httpSession.setAttribute("loggedUser", your_user_object reference_here );
Here is how you access from other placess:
httpSession.getAttribute("loggedUser");//return type is Object here

If I have a handler interceptor that loads a user object, adds it to request attri, do I cast to get object?

I plan to create a Handler interceptor that will fire before the controller gets called (or I'll do this pre-action firing).
I will then check the user's cookie, and load the user object based on the sessionid in the cookie.
I will then add the user object to the request attributes.
Now if I want to retrieve the user object in my controllers action, do I cast it to (User) ?
I believe in my freemarker template I can just do ${user.name} correct? or is it user.getUsername ?
First, you'd better place the user in the session, so that the cookie > user conversion does not happen on each request.
Second, you can just get it from there (session/request) by calling
User user = (User) session.getAttribute(USER_KEY); // this is s String constant
Alternatively, you can make a class UserHolder, where you pass an HttpSession and it gives you the user, thus sparing the casts in your controller cote.
The same approach can be used with HttpServletRequest.

Design question - Persistent data in a webapp session

I am developing a web app using servlets and jsps. I have a question about storing data I need to use across multiple servlets in a login session. When the user logs in, for example, I get the user object from the db and would like to store it somewhere and have the subsequent servlets and jsps use it without having to query the db again. I know that I have to store the object in a global array but am not able to figure out the best way to do this.
I am thinking of having a static hashmap or some other data structure created at webapp load time and I can use that to store the user object with the sessionID as the key for the hashmap.
Is there a better way? Any help is appreciated.
Thanks,
- Vas
You don't need to manage the sessions yourself. The servletcontainer will do it for you transparently in flavor of HttpSession. You normally use HttpSession#setAttribute() to store an object in the session scope and HttpSession#getAttribute() to get an object from the session scope. You can use HttpServletRequest#getSession() to get hold of a reference to the HttpSession.
E.g. in the login servlet:
User user = userDAO.find(username, password);
if (user != null) {
request.getSession().setAttribute("user", user);
} else {
// Show error?
}
You can get it back later in any servlet or filter in the same session by
User user = (User) request.getSession().getAttribute("user");
if (user != null) {
// User is logged in.
} else {
// User is not logged in!
}
You can even access it by EL in JSP:
<p>Welcome, ${user.username}!
(assuming that there's a Javabean getUsername() method)
There is a way to do this and it's defined in the servlet spec. You can get hold of the HttpSession object and add objects as "attributes".
Take a peek at the API here: http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/http/HttpSession.html
Depending on your needs and implementation, you can also consider following options:
making user object serializable and storing in session itself; in this case you must assure that subsequent changes to user object are propagated to the objected stored in session or DB (depending which will change)
storing only user ID in session and implement caching in your DAO/repository so no real DB query will be invoked if not necessary; if you are using Hibernate or some other ORM you might have this feature out of the box; this seems the least invasive as modifications on user object will be synchronized with application state and DB if properly handled by persistence layer
There are probably many more option out there.
We are constructing a social network like livemocha.com and we recommend you put the minimum possible in the session.
Storing only user ID in the session it's enough, and certainly, you don't need to assure that subsequent changes to the user object are propagated to the object stored in the session or DB (depending on which one will change). ;-)

Categories