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). ;-)
Related
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
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
I have a web app that uses a Java Bean for login functions right now and all of the JSP pages check the Bean to make sure the user is logged in. I am also introducing some AJAX functionality now with servlets and I see that of course those exchanges don't check authentication. I'm wondering how I should handle this. For example, I don't want someone to be able to logout, hit back button, then submit something with the AJAX functions successfully.
I can't access the bean from the servlet to check the login (totally wrong context and static vs non-static). I guess I could set a flag with the user entry in the database table denoting logged in or not. Then I can detect timeout logoffs and update the flag as well. But that way would require extra database accesses every time something is done. It would duplicate functionality in some way, but I guess I could perhaps use that just for the AJAX stuff. One difference with that would be the user would not be able to be logged in on multiple places at once as currently.
How is this kind of thing normally done?
Thanks for any help!
You could use session to store that flag instead of the database, and when the user logs out you should remove that flag and destroy the session. In login method
HttpSession session = req.getSession(true);
session.setAttribute("loggedIn",true)
And in your AJAX code
if(eq.getSession(true).getAttribute("loggedIn")==true)
doWork();
else
error("not logged in");
The webcontainer will handle timeouts for you, keep track of each user and his session, and so on.
But I would recommend that you use a standard for managing authntication
I can use jsp to write a web application, I can use jsp to execute a SQL statement to maintain database, why I need a session bean? What make the session bean so special? thank you.
The session bean allows you to carry user-specific information around from page to page, instead of you having to look up a cookie in a database to get the logged in user every time a JSP-page is served.
In other words, it is the facility that allows users to log in to your web site.
It holds data through out the session,
For Example :
In your Web Application you have user name , user profile data that you need to display on each page.
It is obviously not good practice to fetch it from DB for each page better to take it once and store it in session bean
Personally I would never put sql statement execution ON a jsp. that should be down in the model somewhere.
Let's say I have a running Java-based web application with 0 or more valid HttpSession objects associated with it. I want a way to access the current list of valid HttpSession objects. I was thinking that I could implement an HttpSessionListener and use it to append to a list of session id values that are stored in an application-scoped attribute, but then I'm on the hook to update the list as sessions are invalidated and who knows what else.
Before I start baking my own solution I thought I should ask the question:
Does the servlet API provide some means of getting access to the complete list of non-invalidated session objects?
I am using Tomcat 6.x as my web application container, and the MyFaces 1.2.x (JSF) library.
SOLUTION
I followed an approach similar to what BalusC discussed in these existing questions:
How to easily implement "who is
online" in Grails or Java Application
?
JSF: How to invalidate an
user session when he logs twice with
the same credentials
I modified by SessionData class to implement HttpSessionBindingListener. When a binding event happens, the object will either add or remove itself from the set of all the SessionData objects.
#Override
public void valueBound(HttpSessionBindingEvent event) {
// Get my custom application-scoped attribute
ApplicationData applicationData = getApplicationData();
// Get the set of all SessionData objects and add myself to it
Set<SessionData> activeSessions = applicationData.getActiveSessions();
if (!activeSessions.contains(this)) {
activeSessions.add(this);
}
}
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
HttpSession session = event.getSession();
ApplicationData applicationData = getApplicationData();
Set<SessionData> activeSessions = applicationData.getActiveSessions();
if (activeSessions.contains(this)) {
activeSessions.remove(this);
}
}
The one thing that continues to irritate me is what happens when Tomcat is restarted. Unless Tomcat has been properly configured to NOT serialize sessions to disk, it will do so. When Tomcat starts up again, the HttpSession objects (and the SessionData objects along with them) are deserialized and the sessions are made valid again. However, the serialization/deserialization totally sidesteps the HttpSession listener events, so I do not have the opportunity to gracefully put the deserialized reference to the SessionData back in my managed Set of objects after the restart.
I don't have any control over the production configuration of Tomcat in my customer's organization, so I cannot assume that it will be done the way I expect it.
My workaround is to compare the HttpSession creation time with the application startup time when a request is received. If the session was created before the application startup time, then I call invalidate() and the user is sent to an error/warning page with an explanation of what happened.
I get the application startup time by implementing a ServletContextListener and storing the current time inside an application-scoped object from within the contextInitialized() method of my listener.
No, the Servlet API doesn't provide a way. You really have to get hold of them all with help of a HttpSessionListener. You can find several examples in the following answers:
How to find HttpSession by jsessionid?
How to find number of active sessions per IP?
How to check Who's Online?
How to invalidate another session when a user is logged in twice?
There is no straight forward way. It depends on deployment. Above will fail once you decide to introduce distributed deployment and load balancing.
Not really an answer, but in the good ol' days there was "javax.servlet.http.HttpSessionContext", but it was dropped as of version 2.1, explicitly with no replacement: https://tomcat.apache.org/tomcat-5.5-doc/servletapi/javax/servlet/http/HttpSessionContext.html