Vaadin session setMaxInactiveInterval UI response inconsistant - session

I have set the max inactive interval for Vaadin session as following.
VaadinSession.getCurrent().getSession().setMaxInactiveInterval(60);
Added a session destroy listener as following for testing.
servletService.addSessionDestroyListener(new SessionDestroyListener() {
public void sessionDestroy(SessionDestroyEvent event) {
System.out.println("SESSION TIMEOUT");
}
});
This listener get called at the desired time on the server side.
However I cannot see "Session Expired" message on the browser side at the same time. Normally it gets displayed between 4th and 5th minutes.
Is there a way to get both of these at the same time in a consistent manner.
Also note that we are not using push and it is not an option for us at the moment.
Doing client side polling will reset last active time of the sessions and can keep the session active forever if poll interval is lesser than maxInactiveInterval.

Vaadin application keeps Client Side and Server Side communication during session life-cycle. There is parameter calls heartbeatInterval with default value 5 mins (300s). So it is means that every 5 mins Client Side ask Server if session still alive. That is why, when Session is destroyed you see message in Console and only after some period of time you see Session Expired message in a Browser.
You can change heartbeatInterval property and set smaller value (in seconds), but remember that you need to set closeIdleSessions=true explicitly as well. In example below I set this value to 1 second.
#VaadinServletConfiguration(heartbeatInterval=1, closeIdleSessions=true, productionMode = false, ui = MyUI.class)
public static class Servlet extends VaadinServlet {
}

The problem :
Your session is invalidated on the server side, everything is good. But the problem is that your client is never notified of this event. You have to do an interaction with the server to get a Session Expired message, like pressing the button, refreshing the page, etc...
How to fix this?
To fix this, you have some solutions:
Use #Push annotation see Documentation
Force client-side refresh using getPage().reload()
Change nothing and your session timeout will appear on next Client-Side action
Implement a "Session lookup" on client side to watch every X seconds if the session is still valid, if it is expired, simply call Page.getCurrent.reload() from client side.
Be careful with #Push
Depending on the application server you are Using, you may need to update it to support #Push (I had to with tomcat7, because tomcat7 doesn't support WebSocket)

Following solution worked for me in this scenario of not having #Push enabled as well as without any custom js widgets.
First set maxInactiveInterval as following in the main class. Showing only the code related to this solution.
public class MyApplication extends UI {
#Override
protected void init(VaadinRequest request) {
VaadinSession.getCurrent().getSession().setMaxInactiveInterval(sessionTimeout);
}
}
We have to default session expired message as following. This has been suggested in a Vaadin forum as a solution and it said it should work within 15 seconds of session timeout.
public class CustomInitServlet extends VaadinServlet {
getService().setSystemMessagesProvider(
new SystemMessagesProvider() {
#Override
public SystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo) {
CustomizedSystemMessages customizedSystemMessages = new CustomizedSystemMessages();
customizedSystemMessages.setSessionExpiredMessage(null);
customizedSystemMessages.setSessionExpiredCaption(null);
customizedSystemMessages.setSessionExpiredNotificationEnabled(true);
return customizedSystemMessages;
}
});
// other code
}
Then in the web.xml added high heartbeat interval which will be higher than the maxInactiveInterval.
<context-param>
<param-name>heartbeatInterval</param-name>
<param-value>1200</param-value>
</context-param>

Related

Only one User using entire web application at a time - Spring boot

In spring boot application only one user should be using the certain page at a time (let's call it home.jsp). Another users should be redirected to different page(let's call it another_home.jsp) if they appear when accessing that same url. User doesn't login and just uses the application as it is. Any policy can be used for home.jsp could be first-come-first-serve or any other.
If more than one users are using application at a time only one user should be using home.html and all rest of the others should be using another_home.jsp.
As no login is needed in the application I believe I need anonymous sessions. Also, session needs to be expired after some time of inactivity. I've searched spring security but couldn't find anything.
I think that you don't even need spring security. Simple http session will work too. As far as I can see you just want to allocate the stream to one user and for that you need first user's session id which you can compare against whenever the requests come again. So store session id and expire after some timeout with some Time object or Date object.
In properties
server.servlet.session.timeout = 600 // 10 minutes
Something like this
private String currSessionId = null;
private Date lastDate = new Date();
private Integer TIMEOUT = 600000; // 10 minutes
public String loadHomePage(Model model) {
if(currSessionId!=null && new Date().getTime()- lastDate.getTime()>TIMEOUT){
currSessionId = null;
}
if(currSessionId==null){
currSessionId = session.getId();
lastDate = new Date();
return "home";
}else{
if(session.getId().equals(currSessionId)){
return "home";
}else{
return "another_home";
}
}
}
This is as simple as it gets when you don't have logged in users to manage and also don't need to remember previous state where user left off. Let me know if it helps.
You need to create a serverside state that is either empty or stores the identifier of the visitor that is currently claiming /home.jsp.
This could be a field on a singleton Bean, or an entity in the database.
It has to expire automatically, or it will prevent new visitors forever to make a claim.
As long as the state is empty, the first visitors identifier will be stored in this state.
And from that moment on, you will redirect all other visitors to another_home.jsp
So the Controllers Code would be something like this
if(visitorHoldsTheClaim()) {
return "home.jsp"
} else if (noClaimActive()) {
createClaimForVisitor();
return "home.jsp"
} else {
return "redirect:/another_home.jsp"
}
Depending on your implementation, these methods will do different things.
I'd usually recommend against serverside session state (more about this in Roy Fieldings Dissertation),
but for your use case, you need a way to identify a visitor over many requests.
A session would certainly be a very simple way to achieve this.
You can at least minimize session usage by only creating one session at a time - the one for the visitor that holds the claim.
In this case you'd never have more than one open session, and the visitor that owns the session is the visitor that holds the claim.
So in this case, the implementation would be be something like this:
if(currentUserHasASession()) { // checks if the current user has a session, but !!!does not create a new session if it does not exist!!! careful, HttpServletRequest.getSession(true) would create it!
return "home.jsp"
} else if (serverHasNoSessions()) { // https://stackoverflow.com/questions/49539076/how-can-i-get-a-list-of-all-sessions-in-spring
createSessionForUser(); // HttpServletRequest.getSession(true)
return "home.jsp"
} else {
return "redirect:/another_home.jsp"
}
Keep in mind that this only works if you do not create Sessions in another place.
So you have to configure Spring Boot/Spring Security to not create Sessions. How to make spring boot never issue session cookie?
Also keep concurrency in mind. For example, if you had only one server instance, you could put this code into a synchronized method to avoid two visitors creating a claim at the same time.
So... first of all, this sounds like a bad idea. I would be curious why you would need such an unusual behavior. There might be more sensible approaches for it.
Like Gregor said, the redirect code part is rather straightforward:
if(pageLock.getUser() == null) {
pageLock.setUser(user);
}
if(user.equals(pageLock.getUser())) {
return "home.jsp"
} else {
return "redirect:/another_home.jsp"
}
What is actually more tricky is the part when "expiring" the lock. It's likely the user will simply close the browser and not click on "logout" (or whatever), leaving the lock forever. On the other extreme, the user might be gone for a lunch break but its browser still has the page open for hours.
So that's the first thing you wanna add: some keep-alive mechanism on the page, regularly prolonging the lock, and some expiration checker, releasing the lock if nothing was received for a while.
...but like I said in the beginning, the whole thing sounds fishy.

Perserving TestSecurityContextHolder during pure binary websocket connection in Spring Boot test

I have an spring boot (1.5.2.RELEASE) app that is using binary websocket (i.e. NO Stomp, AMQP pure binary buffer). In my test I am able to send messages back and forth which works just great.
However I am experiencing the following unexplained behaviour related to TestSecurityContexHolder during the websocket calls to the application.
The TestSecurityContextHolder has a context that is begin set correctly i.e. my customer #WithMockCustomUser is setting it and I can validate that when putting a breankpoint in the beginning of the test. I.e.
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser>,
That works great and I am able to test server side methods that implement method level security such as
#PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
public UserInterface get(String userName) {
…
}
The problem I have starting experiencing is when I want to do a full integration test of the app i.e. within the test i crate my own WebSocket connection to the app, using only java specific annotations i.e. (no spring annotaions in the client).
ClientWebsocketEndpoint clientWebsocketEndpoint = new ClientWebsocketEndpoint(uri);
.
#ClientEndpoint
public class ClientWebsocketEndpoint {
private javax.websocket.Session session = null;
private ClientBinaryMessageHandler binaryMessageHandler;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
public ClientWebsocketEndpoint(URI endpointURI) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, endpointURI);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
….
}
If try calling the websocket then I first see that the “SecurityContextPersistenceFilter” is removing the current SecurityContex which is fully expected. I actually want it to get remove since I want to test authentication anyway, since authentication is part of the websocket communication and not part of the http call in my case, but what bothers me is the following.
So far we had only one HTTP call (wireshark proves that) and the SecurityContextPersistenceFilter has cleared the session only once and by setting a breakpoint on the clear method i see that indeed it has been called only once. After 6 binary messaged (i.e. the SecurityContext is set in the 5 message received from the client) are being exchanged between the client and the server I do authentication with a custom token and write that token to the TestSecurityContextHolder btw SecurityContexHolder i.e.
SecurityContext realContext = SecurityContextHolder.getContext();
SecurityContext testContext = TestSecurityContextHolder.getContext();
token.setAuthenticated(true);
realContext.setAuthentication(token);
testContext.setAuthentication(token);
I see that the hashCode of that token is the same in bought ContexHolders which means that this is the same object. However next time I received a ByteBuffer from the client, the result of SecuriyContextHolder.getAuthentication() is null. I first though that his is related to the SecurityContextChannelInterceptor since i read a good article about websockets and spring i.e. here but this does not seems to be the case. The securityContextChannelInterceptor is not executed or called anywhere at least when putting breakpoints i see that IDE is not stopping there. Please note that I am deliberately not extending the AbstractWebSocketMessageBrokerConfigurer here since i do not need it i.e. this is plain simple binary websocket with no (STOMP AMQP etc. i.e. no known messaging ). However i see another class i.e. WithSecurityContextTestExecutionListener clearing the context
TestSecurityContextHolder.clearContext() line: 67
WithSecurityContextTestExecutionListener.afterTestMethod(TestContext) line: 143
TestContextManager.afterTestMethod(Object, Method, Throwable) line: 319
RunAfterTestMethodCallbacks.evaluate() line: 94
but only when the test finished!!! i.e. that is way after the SecurityContext is null, although manually set with customer token before. It seems that something like a filter (but for websockets i.e. not HTTP) is clearing the securityContext on each WsFrame received. I have no idea what that is. Also what might be also relative is: on the server side when i see the stack trace i can observe that StandardWebSocketHandlerAdapter is being called which is creating the StandardWebSocketSession.
StandardWebSocketHandlerAdapter$4.onMessage(Object) line: 84
WsFrameServer(WsFrameBase).sendMessageBinary(ByteBuffer, boolean) line: 592
In the StandardWebSocketSession i see that there is a field "Principal user". Well who is supposed to set that principal i.e. i do not see any set methods there the only way to set it is is during the "AbstractStandardUpgradeStrategy" i.e. in the first call but then what to do once the session it established? i.e. the rfc6455 defined the
10.5. WebSocket Client Authentication
This protocol doesn't prescribe any particular way that servers can
authenticate clients during the WebSocket handshake. The WebSocket
server can use any client authentication mechanism available
for me that means that i SHOULD be able to define the user Principal in the later stage whenever i want.
here is how to test is runned
#RunWith(SpringRunner.class)
#TestExecutionListeners(listeners={ // ServletTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
WithSecurityContextTestExecutionListener.class
}
)
#SpringBootTest(classes = {
SecurityWebApplicationInitializerDevelopment.class,
SecurityConfigDevelopment.class,
TomcatEmbededDevelopmentProfile.class,
Internationalization.class,
MVCConfigDevelopment.class,
PersistenceConfigDevelopment.class
} )
#WebAppConfiguration
#ActiveProfiles(SConfigurationProfiles.DEVELOPMENT_PROFILE)
#ComponentScan({
"org.Server.*",
"org.Server.config.*",
"org.Server.config.persistence.*",
"org.Server.core.*",
"org.Server.logic.**",
})
#WithMockCustomUser
public class workingWebSocketButNonWorkingAuthentication {
....
here is the before part
#Before
public void setup() {
System.out.println("Starting Setup");
mvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.build();
mockHttpSession = new MockHttpSession(webApplicationContext.getServletContext(), UUID.randomUUID().toString());
}
And in order to summarize my question is what could be causing the behaviour where Security Context returned from the bought TestSecurityContextHolder or SecurityContextHolder is null after another ByteBuffer (WsFrame) is being received from the client?.
#Added 31 May:
I found by coincidence when running the test mulitple times that sometimes the contex is not null and the test OK i.e. sometimes the contex is indeed filled with the token i supplied. I guess this has something to do with the fact that the Spring Security Authentication is bound to a ThreadLocal, will need further digging.
#Added 6 June 2017:
I can confirm know that the problem is in the threads i.e.the authentication is successful but when jumping between http-nio-8081-exec-4 to nio-8081-exec-5 the Security Contex is beeing lost and that is in the case where i have set the SecurityContextHolder Strategy to MODE_INHERITABLETHREADLOCAL. Any sugesstions are greatly appreciated.
Added 07 June 2017
If i add the SecurityContextPropagationChannelInterceptor does not propagate the security Context in case of the simple websocket.
#Bean
#GlobalChannelInterceptor(patterns = {"*"})
public ChannelInterceptor securityContextPropagationInterceptor()
{
return new SecurityContextPropagationChannelInterceptor();
}
Added 12 June 2017
did the test with the Async notation i.e. the one found here. spring-security-async-principal-propagation . That is showing that the Security Context is being transferred correctly between methods that are executed in different threads within spring, but for some reason the same thing does not work for Tomcat threads i.e http-nio-8081-exec-4 , http-nio-8081-exec-5 , http-nio-8081-exec-6 , http-nio-8081-exec-7 etc. I have the feeling that his has something to do with the executor but so far i do not know how to change that.
Added 13 June 2017
I have found by printing the current threads and the Security Contex that the very first thread i.e. http-nio-8081-exec-1 does have the security context populated as expected i.e. per mode MODE_INHERITABLETHREADLOCAL, however all further threads i.e http-nio-8081-exec-2, http-nio-8081-exec-3 do not. Now the question is: Is that expected? I have found here working with threads in Spring the statement that
you cannot share security context among sibling threads (e.g. in a thread pool). This method only works for child threads that are spawned by a thread that already contains a populated SecurityContext.
which basically explains it, however since in Java there is no way to find out the parent of the thread , I guess the question is who is creating the Thread http-nio-8081-exec-2 , is that the dispatcher servlet or is that tomcat somehow magically deciding now i will create a new thread. I am asking that because i see that sometimes parts of the code are executed in the same thread or in different depending on the run.
Added 14 June 2017
Since i do not want to put all in one i have created a separated question that deals with the problem of finding the answer how to propagate the security context to all sibling threads created by the tomcat in case of a spring boot app. found here
I'm not 100% sure I understand the problem, but it's unlikely that the Java dispatcher servlet will create a new thread without being told to. I think tomcat handles each request in a different thread, so that might be why the threads are being created. You can check this
and this out. Best of luck!

Strategy for logging Tomcat expired sessions

My web application logs user logins and logouts on a database table, so that we keep track of user activity.
This works well when users explicitly login or logout, since the underlying authentication code is responsible for these logging events.
However, when the user leaves the computer idle for 30 minutes, the web session will expire and we'll have one Login event without the corresponding Logout event.
The only strategy I've thought for resolving this is to have a javascript event that triggers before the 30 minutes defined in the web.xml and that kicks the user out before the session expires. However, that has the limitation of needing javascript, and when someone uses multiple tabs it can have some unexpected results (i.e., user is using tab2 and session is expired in tab1).
What would you suggest for this case?
You want to use an HttpSessionListener. It's a standard interface defined by the Servlet Specification for exactly this kind of situation.
See below for a caveat.
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class LogoutListener implements HttpSessionListener {
#Override
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = event.getSession();
// NOTE: You need to store some kind of user identification
// in the session. My example just has a "username"
// in here.
String username = session.getAttribute("username");
// If the username is null, there was no logged-in user
// to log-out (e.g. non-authenticated user).
if(null != username) {
// NOTE: Obviously, this needs to call your existing
// "logout"-recording code. Just an example.
MyDBUtilities.registerLogout(username, System.currentTimeMillis());
}
}
#Override
public void sessionCreated(HttpSessionEvent event) {
// Ignore
}
}
If you use the code above, you'll find that users who don't explicitly log out will get a single logout record, and users who do explicitly log out will get two. Why? Because you are likely using your "logout" action to write the "logout" record and then the session listener runs immediately afterward and makes a second audit entry.
There are two obvious solutions to this problem:
Stop recording the logout event in your "logout" action, and let the event handler log all logouts
Remove the user's "username" session attribute when you log them out and the event handler will ignore the event
use a session listener to update the database when the session expires.
This is a simple example of how to implement session.
https://www.mkyong.com/servlet/a-simple-httpsessionlistener-example-active-sessions-counter/

How to prevent AJAX polling keeping Asp.Net sessions alive

We have a ASP.Net application that has been given a 20 minute sliding expiry for the session (and cookie).
However, we have some AJAX that is polling the server for new information. The downside of this of course is that the session will continue indefinitely, as it is being kept alive by the polling calls causing the expiry time to be refreshed. Is there a way of preventing this - i.e. to only allow the session to be refreshed on non-ajax calls?
Turning off sliding expiry is not really an option as this is an application that business users will be using for most of their day between telephone calls.
Other Stackoverflow discussions on this talk about maintaining 2 separate application (one for authenticated calls, one for unauthenticated. I'm not sure this will be an option as all calls need to be authenticated.
Any ideas?
As this question is old I am assuming it has been resolved or a workaround implemented. However, I wanted to mention that instead of AJAX polling the server to perform an operation we have utilized SignalR which allows both the client to communicate with the server via JQuery and/or the server to notify the client.
Check it out: Learn About ASP.NET SignalR
add below code to your controller action that you are reference for polling.Convert this into an attribute so it can be used everywhere. This line will not extend session timeout
[HttpPost]
public ActionResult Run()
{
Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
return Json("");
}
There is no way to stop the ajax from keeping the session and cookies alive!
However, there is a way to achieve what you want to do. That is if the process I will describe will be ok to you.
I think what you really want to achieve is first to refresh your page with ajax so that some processes will be active and running. Also to know when the user has stopped operating the program.
If that is what you want then there is a simple process to achieve this
You will have your ajax running for the things you want to run.
You will remove the session you want to check if user has stopped operation on the page and manage the session as a variable instead.
The variable can be a global variable or a class variable that will be set to initial value whenever the user clicks an element on the page.
(You will select the click event of an element and set the variable to initial value)
You will increment the variable every given time (say every time your ajax runs)
You will also have a function/method run to check the value of that variable if it is greater than the value you set as limit. This can run every time your ajax runs or every time you want it to run (timed event).
If the value of your variable is greater than the limit set it should invalidate or clear session/log user out.
This way if user stops operating (clicking elements) the system on any page that this is running will eventually log out the current user and stop running the program.
I have done this by creating a hidden page in an i-Frame. Then using JavaScript it posts back every 18 minutes to keep the session alive. This works really well.
This example is from a ASP.NET Forms project but could be tweaked for MVC.
Create a page called KeepSessionAlive page and add a meta refresh tag
meta id="MetaRefresh" http-equiv="refresh" content="21600;url=KeepSessionAlive.aspx"
In the code behind
protected string WindowStatusText = "";
protected void Page_Load(object sender, EventArgs e)
{
//string RefreshValue = Convert.ToString((Session.Timeout * 60) - 60);
string RefreshValue = Convert.ToString((Session.Timeout * 60) - 90);
// Refresh this page 60 seconds before session timeout, effectively resetting the session timeout counter.
MetaRefresh.Attributes["content"] = RefreshValue + ";url=KeepSessionAlive.aspx?q=" + DateTime.Now.Ticks;
WindowStatusText = "Last refresh " + DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();
}
Add the hidden iFrame in a master page
iframe ID="KeepAliveFrame" src="KeepSessionAlive.aspx" frameBorder="0" width="0" height="0"
Download example

Change session time with service

I would like to create a service/servlet that gets a time in minutes and change the session timeout value, meaning I want to change
<session-config>
<session-timeout>1</session-timeout>
</session-config>
I know that
request.getSession(true).setMaxInactiveInterval(seconds);
changes only the current seesion and not all sessions.
Without somehow extending and depending upon Tomcat's own classes, I wouldn't know how you could change the default <session-timeout> programmatically once this has been set.
An alternative method might be to store a mutable session timeout value separately (perhaps in the database) - which your service could be responsible for updating/retrieving. You could then use a HttpSessionListener to modify the timeout for newly created sessions based on this value.
For example:
public class SessionTimeoutModifier implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event) {
int timeout = sessionService.getSessionTimeout(); // Gets your value
event.getSession().setMaxInactiveInterval(timeout); // Override default
}
...
A web front end (servlet) could then be developed which could mutate your session timeout via sessionService.setSessionTimeout(value).
Not the exact answer you're looking for but one possible way of achieving it.

Resources