I would like to know how to crete a user session object that can be used across all user requests. The user can select profiles after login, so I need to update that session object with this profile data.
How is it done? How do I initialize a session scoped bean? How can I change objects in that SessionScoped bean later (it is necessary to remember some user actions in his session).
I would be very happy if you could help me in that matter :)
#SessionScoped #Named
public class UserSession implements Serializable {
private ExtendedUserPrincipal extendedUserPrincipal;
#PostConstruct
private void instantiateSession() {
extendedUserPrincipal = new ExtendedUserPrincipal();
}
public void setUserPrincipal(UserPrincipal userPrincipal) {
extendedUserPrincipal.setUserPrincipal(userPrincipal);
}
public void setUser(User user) {
extendedUserPrincipal.setUser(user);
}
public void setUserSecurityData(UserSecurityData userSecurityData) {
extendedUserPrincipal.setUserSecurityData(userSecurityData);
}
#Produces #AuthenticatedUser
private ExtendedUserPrincipal getPrincipal() {
return extendedUserPrincipal;
}
}
I invalidate the session by calling logout() and invalidate() on the session which I get from HttpServletRequest.
I'm injecting the user principal like this. The object should be the same for every user session
#Inject
#AuthenticatedUser
private ExtendedUserPrincipal extendedUserPrincipal;
I'm not sure what all the annotations mean, but you should just be able to put objects in the session manually with your HttpServletRequest object.
request.getSession().setAttribute("userbean", yourBean);
Then if you need to update it just get and set it like any other map
request.getSession().getAttribute("userbean");
The userbean will remain in the session until it is invalidated.
There are many different java libraries with different # annotations, but an annotation is usually just a shortcut for a more basic, manual operation.
I'm not familiar with the specific library/annotations you are using, but from the looks of it, #SessionScoped will just inject the bean into the user's session attributes 'automagically' for you.
A user's session is just a map that is active while a particular user is logged in. You could put any kind of java object in the session attributes map, it doesnt need to be a special kind of object or anything. A 'session scoped bean' is basically fancy words for 'a java object which has been added to the user's session attributes map.'
When the user's session ends, the request.getSession() object is destroyed, along with the objects in the attributes map (so long as they are not referenced anywhere else) thats why they are 'session scoped'.
Also, you might want to try and put #SessionScoped #Named on seperate lines, I'm not sure it will parse them on one line like that.
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'm using a servlet and inside this i need to have an instance of object for each user, usually i would do it using a singleton but since (as far as I know) the servlet itself works as singleton i'm a bit confused:
how can I produce exactly one instance for each session/user?
how can I be sure it will be destroyed o session time-out?
what is the best approach, wait for some events of HttpServlet or act on requests accessing some property to understand what session is calling?
Edit:
The actual scenario is a web application that uses an external component for data access, isn't interesting what this object does for data retrieve, could be db accesses or web service calls i could ignore it.
What i have to create is an instance of this object for each Httpsession and ensure it will be destroyed after the session expire time.
Edit
Seems HttpSessionListener could be a solution: I could create my object associating it in a Map with the sessionid when the session is created and destroy on session expire. Anyone has experience on this?
For simple case you can use HttpSessionListener as follows:
#WebListener
public class MySessionListener implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent se) {
// new session created - add your singleton
Foo f = new Foo();
se.getSession().setAttribute("Foo", f);
// if you need id you can use - se.getSession().getId();
}
public void sessionDestroyed(HttpSessionEvent se) {
// session destroyed - do cleanup
Foo f = (Foo) se.getSession().getAttribute("Foo");
f.destroy(); // your object cleanup method
}
}
If you need to run it in the cluster with session replication, your class should be small and Serializable, otherwise you would need to implement your Map solution and HttpSessionActivationListener to restore its state on other JVM.
Not sure exactly what are you doing, but the simplest way would be to create a client cookie for each started session.
The cookie itself can have an expiration period and you keep on the server track of each session where the key is the generated cookie.
Once the client has a cookie he keeps it using on each request in order to stay in the same session.
Of course you need a way for each user to log in and get his cookie assigned and some server logic to delete not used session ... etc.
Simple solution:
In the description below, Blammy is the per session object name
During login or session creation, add a Blammy object to the session. HttpServletRequest.getSession(false) will return null if there is no session. This means you need to a) create a session and b) add Blammy to the new session.
Post login, check in the session for the Blammy object. If there is no Blammy, react appropriatedly
Use the Blammy object in the session as appropriate.
You can use HttpSessionEvent. On session create add a session attribute with a constant key and your objects instance as value.
I’m trying to develop a Spring MVC application, now I encounter a question. When login successful I add the User entity to session and call http://localhost:8080/user to get the session user. Everything is OK here. But if I call the URL like this http://localhost:8080/user?username=testuser then the session user's username will change to testuser. What should I do that just get current user from session?
The code likes below
Entity:
#Entity
public class User {
private Long id;
private String username;
// ...Getter and Setter...
}
Controller:
#Controller
#RequestMapping("user")
#SessionAttributes("current_user")
public class UserController {
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public User testSession(#ModelAttribute("current_user") User user) {
return user;
}
}
Response of http://localhost:8080/user
[{"id":1,"username":"aaa111"}]
Response of http://localhost:8080/user?username=testuser; it should be same as above, but is
[{"id":1,"username":"testuser"}]
The #SessionAttributes annotation isn't intended for this. Its intend is to store objects in the session during http requests. Imagine a lengthy database call to retrieve an object you don't want to retrieve this object each time but probably reuse an existing one. The object is to be intended to be used as a #ModelAttribute, this annotation indicates that you want to use this object for binding (i.e. you have a form to change attributes of the object). When you are finished with the editing of the object you should make this clear by calling setComplete() on the SessionStatus object. See also here.
You want to store an object in the session and retrieve it when you need it. For this use the HttpSession in the normal way of calling setAttribute and getAttribute. To obtain the current HttpSession you can simply add a method argument of the type HttpSession and it will be injected for you. (See here for a list of supported method arguments).
public void myRequestHandlingMethod(HttpSession session) {
User currentUser = (User) session.getAttribute("currentUser");
}
Or as you are already using Spring you could use the WebUtils for convenience. You can use the getSessionAttribute or getRequiredSessionAttribute methods to obtain the value from the session.
public void myRequestHandlingMethod(HttpServletRequest request) {
User currentUser = (User) WebUtils.getSessionAttribute("currentUser", request)
}
Another solution would be to extend Spring MVC. Spring MVC uses a HandlerMethodArgumentResolver to handle all the different types of method arguments. This mechanism is pluggable. You could create an annotation #CurrentUser and create a CurrentUserHandlerMethodArgumentResolver that will retrieve the user from the session and injects it in that place. You could then simply add your current user to your method signature.
public void myRequestHandlingMethod(#CurrentUser User user) { ... }
Configure the custom argument resolver
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="com.yourcomponany.app.web.CurrentUserHandlerMethodArgumentResolver />
</mvc:argument-resolvers>
</mvc:annotation-driven>
It also looks like you are rolling your own security framework, which I would advice against. Instead I would suggest using Spring Security instead. Advantage of this is that this provides integration with the Servlet API allowing for retrieval of the current Principal by either doing it yourself (request.getUserPrincipal()) or simply adding a method argument of the type java.security.Principal. It also comes with a custom HandlerMethodArgumentResolver which allows you to obtain the current Spring Security Authentication object.
try to get session value in controller from servlet request like below
#Controller
#RequestMapping("user")
#SessionAttributes("current_user")
public class UserController{
#RequestMapping(method=RequestMethod.GET)
#ResponseBody
public User testSession(HttpServletRequest request){
//false means do not create new session
HttpSession session = request.getSession(false);
return session != null?session.getAttribute("current_user"):null;
}
}
here is the code that can reproduce this issue:
#Controller
public class FirstController {
#RequestMapping(value = "firstpage", method = GET)
public String myHander(HttpSession httpSession) {
if (httpSession.getAttribute("someClass") == null) {
httpSession.setAttribute("someClass", new SomeClass());
}
return "firstpage";
}
}
the first controller puts something in the session if it's not already there.
#Controller
#SessionAttributes(types = SomeClass.class)
public class SecondController {
#RequestMapping(value = "secondpage", method = GET)
public String myHandler(SomeClass someClass, HttpSession httpSession) {
//asking spring for the SomeClass parameter, that's why we put it in the annotation.
System.out.print(someClass.hashCode());
httpSession.invalidate();
return "secondpage";
}
}
the second controller kills the session.
and in both jsp files, i have the following code that prints the hashcode of the session object and the hashcode of the session attribute:
session hash:
<%= session.hashCode() %>
<br/>
someclass hash:
<%= session.getAttribute("someClass").hashCode() %>
now if i run the application and visit "firstpage", i'll get this:
session hash: 1838367636
someclass hash: 1075505853
and then i visit the "secondpage", and will get this:
session hash: 842656294
someclass hash: 1075505853
we can see that the session itself is changed, because the second controller kills the session. but the session attribute(of the type SomeClass) remains the same.
then if i try to revisit the "secondpage", the session object changes every time, but the session attribute remains the same.
why does the session attribute(which is supposed to be attached to the session) have a longer lifecycle than the session itself?
PS:the complete code is here:https://github.com/cuipengfei/One-hundred-thousand-why/tree/master/20130701SessionAttributesLifeCycle/SpringMVC
you can run it with mvn jetty:run to reproduce the issue.
The documentation for this annotation isn't particularly clear, but my understanding is that it's used to share values between methods in the same controller. As I read the following:
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
I interpret it to mean that each controller method invocation will be wrapped with a call to (1) load the session attributes before entry, and (2) store them at exit. Which would have the behavior that you're seeing.
This is reinforced (imo) by the following in the annotation's javadoc:
For permanent session attributes, e.g. a user authentication object, use the traditional session.setAttribute method instead
If they're telling you to use the standard functions to modify the session, it indicates to me that the annotation isn't simply a way to access items in the session.
And for a final data point: if this annotation were the way to manage session data, why have session-scoped beans?
Is there any way under spring 3.0 to access the HttpSession without including it in the method signature? What I really want to do is be able to pass in values from an HttpSession that CAN BE null.
Something like this:
#RequestMapping("/myHomePage")
public ModelAndView show(UserSecurityContext ctx) {}
instead of this:
#RequestMapping("/myHomePage")
public ModelAndView show(HttpSession session) {
UserSecurityContext ctx = (UserSecurityContext) session.getAttribute("userSecurityCtx");
}
The #SessionAttribute annotation mentioned by #uthark is not suitable for this task - I thought it was too, but a bit of reading shows otherwise:
Session attributes as indicated using
this annotation correspond to a
specific handler's model attributes,
getting transparently stored in a
conversational session. Those
attributes will be removed once the
handler indicates completion of its
conversational session. Therefore, use
this facility for such conversational
attributes which are supposed to be
stored in the session temporarily
during the course of a specific
handler's conversation.
For permanent session attributes, e.g.
a user authentication object, use the
traditional session.setAttribute
method instead. Alternatively,
consider using the attribute
management capabilities of the generic
WebRequest interface.
In other words, #SessionAttribute is for storing conversation MVC-model objects in the session (as opposed to storing them as request attributes). It's not intended for using with arbitrary session attributes. As you discovered, it only works if the session attribute is always there.
I'm not aware of any other alternative, I think you're stuck with HttpSession.getAttribute()
You can use a RequestContextHolder:
class SecurityContextHolder {
public static UserSecurityContext currentSecurityContext() {
return (UserSecurityContext)
RequestContextHolder.currentRequestAttributes()
.getAttribute("userSecurityCtx", RequestAttributes.SCOPE_SESSION));
}
}
...
#RequestMapping("/myHomePage")
public ModelAndView show() {
UserSecurityContext ctx = SecurityContextHolder.currentSecurityContext();
}
For cross-cutting concerns such as security this approach is better because you doesn't need to modify your controller signatures.
Yes, you can.
#SessionAttributes("userSecurityContext")
public class UserSecurityContext {
}
#RequestMapping("/myHomePage")
public String show(#ModelAttribute("userSecurityContext") UserSecurityContext u) {
// code goes here.
}
See for details:
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/SessionAttributes.html
http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/ModelAttribute.html