Shiro's SessionDAO is used to store session in a db(as a blob).
This session is used across multiple web applications(shiro enabled)
Now the problem is each web application is trying to set attributes(custom classes) in the session.
when tried to deserialize in other web application, it throws back ClassDefNotFoundException as it doesn't exist.
Is there any way I can solve this?
What should be the ideal approach?
Separate your singular session into multiple sessions. Start with a globally accessible session that contains the user's principal and the application specific keys for all other web applications that are using the same session (and nothing else). Then when accessing an isolated web application create a new "session" in the cache and put the key for it into the global session. Then when a user accesses the isolated web application a filter should retrieve the key from the global session and bind the isolated web application specific session to the current thread context (and then remove it after execution has completed).
main-user-session
|
+--user-principal
|
+--app1-user-session-key
|
+--app2-user-session-key
void filter (HttpRequest req, HttpResponse resp) {
var app1key = sessionCache.get("main-user-session-key")
.get("app1-user-session-key");
Session app1Sess = sessionCache.get(app1key);
threadContext.bind("SESSION", app1Sess);
try {
execute(request, response);
} finally (Exception e) {
threadContext.unbind("SESSION");
}
}
Related
This question already has answers here:
How do servlets work? Instantiation, sessions, shared variables and multithreading
(8 answers)
Closed 7 years ago.
This is a pretty remedial question. I have not used the HttpSession class before. I am reading this tutorial, and I see that the session is a property of the HttpServletRequest.
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// get current session, or initialise one if none
HttpSession sess = req.getSession(true);
}
My question is, how does the session get stored? On the client? In the past I have been accustomed to storing the session server / database side. How does this work? If I update the session on a given request, will that always be reflected through subsequent calls? Is the session stored on the client?
how does the session get stored? On the client? In the past I have
been accustomed to storing the session server / database side. How
does this work?
A session can be defined as a server-side storage of information that is desired to persist throughout the user's interaction with the web site or web application.
Is the session stored on the client?
Instead of storing large and constantly changing information via cookies in the user's browser, only a unique identifier is stored on the client side (called a "session id"). This session id is passed to the web server every time the browser makes an HTTP request (ie a page link or AJAX request). The web application pairs this session id with it's internal database and retrieves the stored variables for use by the requested page.
when ever getSession() method is called it returns session if exists else it create a new session.apart from creating a session it does 5 things which you wont do.
You don’t make the new HttpSession object yourself.
You don’t
generate the unique session ID. You don’t make the new Cookie
object.
You don’t associate the session ID with the cookie. You don’t set
the Cookie into the response
All the cookie work happens behind the scenes.
If I update the session on a given request, will that
always be reflected through subsequent calls?
yes it effects the subsequent calls.
With a session cookie, or if cookies are disabled you're able to see the telltale JSESSIONID parameter. This was at least the case a while ago, and I shouldn't think it has changed.
The HttpSession is by default stored in memory and created/maintained by the web server (jetty, tomcat, ...). Depending on the web server you use you might have options like storing session information into the database.
Here is the tomcat documentation for the session manager[1]
[1] https://tomcat.apache.org/tomcat-7.0-doc/config/manager.html
I'm trying to integrate Hazelcast (lasted version - 3.3) session replication.
Our infrastructure consists of :
Apache 2.0 server for the load balancing
Tomcat 7 servers serving our web application
Our main reasons are:
Duplicate user session for high availability
Atmosphere web-socket PubSub servlet need share the same data in order to make full
broadcasting
integrating Hazelcast to our Environment:
each of the tomcat servers will serve as Hazelcast member
basically Hazelcast WebFilter is the first one that executes and its
wrap with : WebFilter->HazelcastSession->setAttribute() implements
HttpSession interface
each time setAttribute is called Hazelcast sync the session attribute
with the rest of the cluster members.
now - its seems like every spring bean we injecting scoped as session bean don't get replicated .
as a workaround :
Save only basic session information via #Context annotation
Dont use Spring session scope , only Singletons and inject the HazelcastInstance
I can Wrap the relevant data as Hazelcast structures
Also, when i looked on other stackoverflow i saw the following : Spring "session" scope of a bean?
The Spring session does not exactly match the HttpSession, and even
the Spring documentation on the #SessionAttributes annotation says
that it might be stored in the session or "some conversational
storage". I got that from [The Spring docs for 2.5][1] I've basically
quit trying to make sense of it, and just got on with my life, if I
want something stored in the HttpSession, I just have Spring inject
the HttpSession to me, assuming you're using Spring MVC its pretty
easy, instructions on the same page.
[1]:
http://static.springsource.org/spring/docs/2.5.x/reference/mvc.html
Its seems strange , Does Spring session beans not exactly match the HttpSession.setAttribute ?
How spring know how to #Inject the proper bean ?
Maybe Spring save the Beans in an internal data storage and only in
the Injection phase Spring getting the proper element using the same
session id attribute and bind the proper bean.
is there any way to control this behavior ?
Update :
debugging spring-web -> ServletRequestAttributes-> is using the
Server impl HTTPSession (For example - in Dev Jetty - org.eclipse.jetty.server.session.HashedSession)
this way the Bean is update in the HTTPSession but skipping the
HazelcastSession :-(
/**
* Update all accessed session attributes through {#code session.setAttribute}
* calls, explicitly indicating to the container that they might have been modified.
*/
#Override
protected void updateAccessedSessionAttributes() {
// Store session reference for access after request completion.
this.session = this.request.getSession(false);
// Update all affected session attributes.
if (this.session != null) {
try {
for (Map.Entry<String, Object> entry : this.sessionAttributesToUpdate.entrySet()) {
String name = entry.getKey();
Object newValue = entry.getValue();
Object oldValue = this.session.getAttribute(name);
if (oldValue == newValue) {
this.session.setAttribute(name, newValue);
}
}
} catch (IllegalStateException ex) {
// Session invalidated - shouldn't usually happen.
}
}
this.sessionAttributesToUpdate.clear();
}
thanks in advance ,
elad.
I want to implement following solution (described in a image) using Java Web Services
When ever a user request with a valid credentials using web services , a session is created over server and that server (who receives the request ) creates a connection with the other server i.e. Meta Trader's Server.
Here each user has a different session to maintain their connection and a state with meta trader server.
Note:
Currently i am not maintaining any session when a user request instead i am saving the connection object in a
#javax.ws.rs.core.Context
ServletContext servletContext;
MyApplication application = new MyApplication();
servletContext.setAttribute("application", application);
But this solution doesn't serve multiple users naturally. so please anyone has an idea how to solve the issue of serving multiple clients then please reply.
I am using Glassfish and JAX-RS ( Jersery 1.1 ) , JAXB
Simply use the annotation #javax.ws.rs.core.Context to get the HttpServletRequest and use its session within the container in which Jersey is deployed.
The code below is a simple example of a jersey resource that gets the session object and stores values in the session and retrieves them on subsequent calls.
#Path("/helloworld")
public class HelloWorld {
#GET
#Produces("text/plain")
public String hello(#Context HttpServletRequest req) {
HttpSession session= req.getSession(true);
Object foo = session.getAttribute("foo");
if (foo!=null) {
System.out.println(foo.toString());
} else {
foo = "bar";
session.setAttribute("foo", "bar");
}
return foo.toString();
}
}
But you should NOT use RESTful API like this. It meant to be used as web service which is stateless, not web application. Check the following answers which I got the example and advice from
(jersey security and session management)
https://stackoverflow.com/a/922058
https://stackoverflow.com/a/7752250
(How to manage state in JAX-RS?)
https://stackoverflow.com/a/36713305
(Get ServletContext in JAX-RS resource)
https://stackoverflow.com/a/1814788
I have a task to show to the site admin a list of user names and how many tomcat sessions are currently utilized by each user (along with some other support-related information).
I keep authenticated users as the application context attribute as follows (sparing unnecessary details).
Hashtable<String, UserInfo> logins //maps login to UserInfo
where UserInfo is defined as
class UserInfo implements Serializable {
String login;
private transient Map<HttpSession, String> sessions =
Collections.synchronizedMap(
new WeakHashMap<HttpSession, String>() //maps session to sessionId
);
...
}
Each successful login stores the session into this sessions map.
My HttpSessionsListener implementation in sessionDestroyed() removes the destroyed session from this map and, if sessions.size() == 0, removes UserInfo from logins.
From time to time I have 0 sessions showing up for some users. Peer reviews and unit testing show that the code is correct. All sessions are serializable.
Is it possible that Tomcat offloads sessions from memory to the hard drive, e.g. when there is a period of inactivity (session timeout is set to 40 minutes)? Are there any other scenarios where sessions are 'lost' from GC point of view, but HttpSessionsListener.sessionDestroyed() wasn't invoked?
J2SE 6, Tomcat 6 or 7 standalone, behaviour is consistent on any OS.
As this question got close to 5k views, I think it would be beneficial to provide an example of a working solution.
The approach outlined in the question is wrong - it will not handle server restarts and will not scale. Here is a better approach.
First, your HttpServlet needs to handle user logins and logouts, something along these lines:
public class ExampleServlet extends HttpServlet {
#Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String action = req.getParameter("do");
HttpSession session = req.getSession(true);
//simple plug. Use your own controller here.
switch (action) {
case "logout":
session.removeAttribute("user");
break;
case "login":
User u = new User(session.getId(), req.getParameter("login"));
//store user on the session
session.setAttribute("user",u);
break;
}
}
}
The User bean has to be Serializable and has to re-register itself upon de-serialization:
class User implements Serializable {
private String sessionId;
private String login;
User(String sessionId, String login) {
this.sessionId = sessionId;
this.login = login;
}
public String getLogin() { return login; }
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
//re-register this user in sessions
UserAttributeListener.sessions.put(sessionId,this);
}
}
You will also need an HttpAttributeListener to handle session lifecycle properly:
public class UserAttributeListener implements HttpSessionAttributeListener {
static Map<String, User> sessions = new ConcurrentHashMap<>();
#Override
public void attributeAdded(HttpSessionBindingEvent event) {
if ("user".equals(event.getName()))
sessions.put(event.getSession().getId(), (User) event.getValue());
}
#Override
public void attributeRemoved(HttpSessionBindingEvent event) {
if ("user".equals(event.getName()))
ExampleServlet.sessions.remove(event.getSession().getId());
}
#Override
public void attributeReplaced(HttpSessionBindingEvent event) {
if ("user".equals(event.getName()))
ExampleServlet.sessions.put(event.getSession().getId(),
(User)event.getValue());
}
}
Of course, you will need to register your listener in web.xml:
<listener>
<listener-class>com.example.servlet.UserAttributeListener</listener-class>
</listener>
After that, you can always access the static map in UserAttributeListener to get an idea of how many sessions are running, how many sessions each user is using etc. Ideally, you would have a bit more complex data structure warranting its own separate singleton class with proper access methods. Using containers with copy-on-write concurrent strategy might also be a good idea, depending on the use case.
Instead of writing something from scratch, check out psi-probe.
http://code.google.com/p/psi-probe/
This may just be a simple drop in that solves your problems.
Do you find that you get the issue following a restart of Tomcat? Tomcat will serialize active sessions to disk during a successful shutdown and then deserialize on startup - I'm not sure whether this will result in a call to your HttpSessionListener.sessionCreated() as the session isn't strictly created, just deserialized (this may be not be correct(!), but can be tested fairly easily).
Have you also compared your results with the Tomcat managers session stats? It keeps track of the number of active sessions, and should tie up with your figures, if not, you know your code is wrong.
Also, probably unrelated to your issue, but is there a good reason why you are using Hashtable and WeakHashMap? I tend to go with ConcurrentHashMap if i need a thread safe Map implementation, its performance is much better.
When a client visits the webapp for the first time and/or the HttpSession is obtained for the first time via request.getSession(), the servlet container creates a new HttpSession object, generates a long and unique ID (which you can get by session.getId()), and store it in the server's memory. The servlet container also sets a Cookie in the Set-Cookie header of the HTTP response with JSESSIONID as its name and the unique session ID as its value.
As per the HTTP cookie specification (a contract a decent web browser and web server have to adhere to), the client (the web browser) is required to send this cookie back in subsequent requests in the Cookie header for as long as the cookie is valid (i.e. the unique ID must refer to an unexpired session and the domain and path are correct). Using your browser's built-in HTTP traffic monitor, you can verify that the cookie is valid (press F12 in Chrome / Firefox 23+ / IE9+, and check the Net/Network tab). The servlet container will check the Cookie header of every incoming HTTP request for the presence of the cookie with the name JSESSIONID and use its value (the session ID) to get the associated HttpSession from server's memory.
The HttpSession stays alive until it has not been used for more than the timeout value specified in <session-timeout>, a setting in web.xml. The timeout value defaults to 30 minutes. So, when the client doesn't visit the web app for longer than the time specified, the servlet container trashes the session. Every subsequent request, even with the cookie specified, will not have access to the same session anymore; the servlet container will create a new session.
On the client side, the session cookie stays alive for as long as the browser instance is running. So, if the client closes the browser instance (all tabs/windows), then the session is trashed on the client's side. In a new browser instance, the cookie associated with the session wouldn't exist, so it would no longer be sent. This causes an entirely new HTTPSession to be created, with an entirely new session cookie begin used.
I'm maintaining a Java web application.
Looking into the login code it gets an HttpSession out of HttpServletRequest via the getSession() method of HttpServletRequest. (It uses some values in the session for authentication purposes)
However I'm worried about session fixation attacks so after I have used the initial session I want to either start a new session or change the session id. Is this possible?
The Servlet 3.0 API doesn't allow you to change the session id on an existing session. Typically, to protect against session fixation, you'll want to just create a new one and invalidate the old one as well.
You can invalidate a session like this
request.getSession(false).invalidate();
and then create a new session with
getSession(true) (getSession() should work too)
Obviously, if you have an data in the session that you want to persist, you'll need to copy it from the first session to the second session.
Note, for session fixation protection, it's commonly considered okay to just do this on the authentication request. But a higher level of security involves a tossing the old session and making a new session for each and every request.
Since Java EE 7 and Servlet API 3.1 (Tomcat 8) you can use HttpServletRequest.changeSessionId() to achieve such behaviour. There is also a listener HttpSessionIdListener which will be invoked after each change.