How spring session beans saved ? replicating spring session beans via Hazelcast - java

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.

Related

Websession invalidation not working from Spring Boot 2.0.2

I wanted to upgrade my Spring Webflux project from Spring Boot 2.0.1 to Spring Boot 2.0.3. In my project, my sessions are backed by Spring Session Data Redis. When upgrading the Spring Boot version, I noticed a websession invalidation problem from Spring Boot 2.0.2+.
In Spring Boot 2.0.1, I invalidated my sessions this way :
webSession.invalidate().subscribe();
This caused my current session to be destroyed, and a new one was generated with a new session ID, creation time, etc.
However, from Spring Boot 2.0.2, the same code doesn't seem to fully destroy the session. The current session information are just "cleared", and the same session ID is still used afterward. This is a problem because it causes a null pointer exception in ReactiveRedisOperationsSessionRepository.class :
private static final class SessionMapper implements Function<Map<String, Object>, MapSession> {
private final String id;
private SessionMapper(String id) {
this.id = id;
}
public MapSession apply(Map<String, Object> map) {
MapSession session = new MapSession(this.id);
session.setCreationTime(Instant.ofEpochMilli((Long)map.get("creationTime")));
session.setLastAccessedTime(Instant.ofEpochMilli((Long)map.get("lastAccessedTime")));
session.setMaxInactiveInterval(Duration.ofSeconds((long)(Integer)map.get("maxInactiveInterval")));
map.forEach((name, value) -> {
if (name.startsWith("sessionAttr:")) {
session.setAttribute(name.substring("sessionAttr:".length()), value);
}
});
return session;
}
}
As session data are empty (no creation time, etc.), the following line raises a NPE:
session.setCreationTime(Instant.ofEpochMilli((Long)map.get("creationTime")));
Is it a bug or have I missed something new in Spring Boot 2.0.2+ regarding Spring Session?
UPDATE
To provide more information, I created a sample project reproducing the problem: https://github.com/adsanche/test-redis-session
This project contains a simple controller exposing two endpoints:
#Controller
public class HelloController {
#GetMapping(value = "/hello")
public String hello(final WebSession webSession) {
webSession.getAttributes().put("test", "TEST");
return "index";
}
#GetMapping(value = "/invalidate")
public String invalidate(final WebSession webSession) {
webSession.invalidate().subscribe();
return UrlBasedViewResolver.REDIRECT_URL_PREFIX + "/hello";
}
}
Behavior when running the project with Spring Boot 2.0.1
Go to the first endpoint: http://localhost:8080/hello
State of the session in Redis:
We notice the session ID starting with 7546ff, and the session data containing the "test" attribute plus the default session information (creation/last access time, etc.).
Go to the second endpoint: http://localhost:8080/invalidate
The current session is invalidated, and a redirection is performed on "/hello", where the test attribute is added in a new web session.
State of the new session in Redis:
We notice the new session ID starting with ba7de, and the new session data still containing the test attribute plus the default session information.
Now, let's reproduce this scenario on the same project with Spring Boot 2.0.3.
Behavior when running the project with Spring Boot 2.0.3
Go to the first endpoint: http://localhost:8080/hello
State of the session in Redis:
We notice the session ID starting with 12d61, and the session data containing the "test" attribute plus the default session information (creation/last access time, etc.).
Go to the second endpoint: http://localhost:8080/invalidate
State of the new session in Redis:
We notice here that the same session ID is still used, but the session has been cleared even from its default information (creation date, etc.). So, when it is invoked again, a NPE is triggered in the reactive redis session repository in the apply() method at the place I mention in my original post:
Hope this sample project helps to find out if its a bug or an implementation problem on my side.
This is a bug in Spring Session's SpringSessionWebSessionStore, or more specifically, its internal WebSession implementation. The problem is that on WebSession#invalidate session is only cleared from the underlying session store, but not marked as invalid so the subsequent request processing still ends up using the same session id.
I've openend gh-1114 to address this in Spring Session.
I believe you haven't experienced this previously i.e. with Spring Session 2.0.2.RELEASE and below due to another bug in SpringSessionWebSessionStore that was addressed in 2.0.3.RELEASE - see gh-1039. This issue was about failing to update the lastAccessedTime, and once we fixed that the scenario you described started failing due to session for invalidated (and deleted) session id being saved again only with lastAccessedTime attribute.

How does Spring Security handle JSESSIONID with various Session Creation and Session Fixation combinations?

I have a J2EE REST-based app using Spring Security 4.0.1.RELEASE. Needless to say, Spring documentation on sessionCreationPolicy and sessionFixation is sparse, aside from targeted questions here on StackOverflow.
I'm using a Java-based config for Spring Security like this:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(secureEnabled=true, prePostEnabled=true, jsr250Enabled=true, order=1)
public class DefaultSecurityBeansConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.sessionFixation().migrateSession()
.and()...; // additional config omitted for brevity
}
}
I'd really just like to know what behavior to expect from Spring, as it relates to JSESSIONID, given all possible combinations of sessionCreationPolicy and sessionFixation.
Possible values in the SessionCreationPolicy enum are ALWAYS, NEVER, IF_REQUIRED, and STATELESS.
Possible values for session fixation are newSession, migrateSession, changeSessionId, and none.
Thank you.
NOTE: What prompted this question is that I am not seeing a new JSESSIONID on every request when I have sessionCreationPolicy set to IF_REQUIRED and sessionFixation set to changeSessionId. A JSESSIONID is correctly created, but is maintained across requests thereafter. I generalized my question about all combinations to hopefully help others in a similar situation with slightly different settings.
It's important to keep in mind that Spring Security doesn't always have full control of the HttpSession. It can create one itself, but it can also be provided a Session object by the container.
For SessionCreationPolicy.IF_REQUIRED, the docs state:
Spring Security will only create an HttpSession if required
In your particular case, you're not seeing a new JSESSIONID for every request for at least 2 possible reasons:
With your current configuration, Spring has the option of creating a Session if it needs one.
SessionCreationPolicy.IF_REQUIRED also appears to allow Spring Security to use the Session it is provided with. Your container might be providing this object if this is the case, and so the session is maintained across multiple requests (as is expected if you're in a session).
If you wanto to disable #1, use SessionCreationPolicy.NEVER:
Spring Security will never create an HttpSession, but will use the HttpSession if it already exists
The only SessionCreationPolicy that will ensure that Spring Security uses NO SESSIONS is SessionCreationPolicy.STATELESS.
As regards SessionFixation, it only comes into play when you have multiple sessions for one user, after authentication. At this point, the SessionCreationPolicy is somewhat irrelevant.
SessionCreationPolicy: used to decide when (if ever) to create a new session
SessionFixation: once you have a session for a user, what to do with the session if the user logs in again
Hope this helps!

Why use session bean at all if ultimately its the HttpSession that holds the state

I am new to EJB so please don't mind anything silly in the question.
I have a doubt that someone might be able to solve hopefully.
I have the following Stateful Bean:
#Stateful
public class SessionBean implements SessionBeanRemote {
private int count = 0;
#Override
public int getCount(){
count++;
return count;
}
}
And this is the client that invokes the Bean (Servlet)
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
InitialContext ctx;
HttpSession session = null;
SessionBeanRemote obj;
try {
if (session.getAttribute("myBean") == null) {
ctx = new InitialContext();
obj = (SessionBeanRemote) ctx.lookup("SessionBean/remote");
session.setAttribute("myBean", obj);
} else {
obj = (SessionBeanRemote) session.getAttribute("myBean");
}
System.out.println(obj.getCount());
} catch (NamingException ex) {
Logger.getLogger(TestServlet.class.getName()).log(Level.SEVERE, null, ex);
}
}
Now I was wondering if it is ultimately HttpSession that has to hold the session bean then why use EJB at all, why not just store whatever we want to in the session directly rather than having it in the session bean first and then storing that bean in the session.
And also I was wondering if lets say I change my #Stateful annotation to #Stateless and then execute the same code on the client side and store the bean in the session then also I can extract the same bean from the session then what is the difference between stateless and stateful, I know that when new lookup is done there is a chance that same stateless bean might be returned to me where as with stateful bean it is always new when we do a lookup. But is that it?
P.S. As I mentioned earlier, I am new to EJB and all the doubts are based on what I have understood from a few tutorials online and some questions on SO. I have also tried running it locally but unfortunately I am not able to deploy the application on the GlassFish because of following error "Exception while loading the app : EJB Container initialization error". I am trying to look into it.
They're two unrelated concepts.
If you seperate concerns, and you should. Then the HTTP session and EJB session(s) operate at logically distinct layers. The http session is for holding an individual web browser and user's state. An EJB session is used for holding transactional, scalable and fault-tolerant and "transparently" (and possibly remote) reference(s) within the context of an Enterprise application client.
The fact that you're using EJB(s) to serve web content, does not mean you cannot also use those same EJB(s) to serve JFC/Swing (or JavaFX) clients.
I'll try to give you a simplified answer.
Yes, you have to store reference to the SFSB somewhere, in case of web application in http session, but as Elliott wrote you may have different client types also.
Some benefits of Session bean vs POJO:
container transaction management
container security enforcement
possible remote access via remote interface
allows to be deployed as separate module (EJB) for potentially better reusability.
If your session bean relies on state, then it is more logical to hold the state in the bean rather than pass all state info in each method call.
Your example is extremely simple, you don't use transactions, persistence, security, so there is no point of using SFSB or even EJB at all.
SFSB are considered to be rather heavyweight, in general should rather be avoided and I'd say majority of web applications don't use them (depends on application requirements really). So you should design your services to be stateless and rather use stateless beans than stateful, then you will have better performance and easier reusability.
So if you plan to use some of the features I provided, you may benefit from EJBs, otherwise you maybe happy with just Http session and business logic in POJOs.
EJB Sessions
A session in EJB is maintained using the SessionBeans over the server's JVM. You design beans that can contain business logic or calculations or dynamic pages, and that can be used by the clients. You have two different session beans: Stateful and Stateless.
Stateful: is somehow connected with a single client (each bean's object). It maintains the state for that client, can be used only by that client and when the client "dies" then the session bean is "lost". Stateful bean's life-cycle is bind with the client. (very useful in role-based applications/systems)
Stateless: A Stateless Session Bean doesn't maintain any state and there is no guarantee that the same client will use the same stateless bean, even for two calls one after the other. The lifecycle of a Stateless Session EJB is slightly different from the one of a Stateful Session EJB. Is EJB Container's responsability to take care of knowing exactly how to track each session and redirect the request from a client to the correct instance of a Session Bean and to same job for every one.
Where:
HTTPSession: It is acquired through the request object. You cannot really instantiate a new HttpSession object, and it doesn't contains any business logic or calculation, but is more of a place where to store objects (for client as output or server as input), and for communication of two or more system over the network.

shiro session store using dao across multiple web applications

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");
}
}

Spring "session" scope of a bean

It seems to me that "session" scope is another means to keep objects in session as
using setAttrubute / getAttribute
Correct?
You know, dont know why, it does not work for me.
<bean id="sabreUser" class="util.MyUser" factory-method="getSomeUser" scope="session">
<const args...>
What I see is that after the initialization and initial deploy the MyUser properties are correct.
Then, in the first session I change MyUser property, the session is closed.
The second session runs and it sees the last set value from the previous session!
What does that mean?
I expect this object to be initialized whenever a new session starts. But it better looks as singleton, though you see - "session" attribute is set.
I can see the cause of the problem in that a Servlet's fields is initialized with #Autowired
so, once it is initialized, every other session will see its fields set and does not "ReWire" this properties. It happens once? How to overcome this problem?
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
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.
Session-scoped beans are beans that live throughout the http session. They are stored in the session via setAttribute in some way.
So - yes.
Session scoped beans are stored in Http Session by Spring framework. This scope is valid only in the context of Web application.It also works for Portlet envionments . When using in Portlet environment, there are two notions of session, application scope and portlet scope (default).

Categories

Resources