is it possible to specify the sticky session duration in mod_cluster?
I mean that the stuck session is cleared when there isn't activity for a period of time.
We have a distributable application that keeps a reference to logged user in the web session. But at login time, the web session replication isn't enough fast as requests that follow the login request. So, if, for those requests, the balancer choose a node that doesn't have been replicated yet, the user wouldn't be in session and an error occur.
Another use of this functionality would be when you use cached information on a server. If you don't use sticky session you would load to cache several times the same information in different servers. But if you use sticky session you would be tied to the same server for all session life.
Thanks in advance
Leandro
Answer
It is not possible to switch session stickiness in mod_cluster On and Off for a certain time duration. One either has it On or Off.
Further comments
IIUC, you are in fact after session expiration or session invalidation. You can programatically decide to invalidate your session at any moment, or you might let it expire by setting expiration timeout.
Could you perhaps elaborate more on how would you use the "session stickiness timeout"? We could create a JIRA and and implement a new feature if it makes sense...
I´ve found a workaround for our needs.
I configure different stickysession attribute at balancer (BALANCER_SESSION_ID_HEADER_NAME) and manage balancer sticky session duration at client side.
First time, I set counter + JSESSIONID to BALANCER_SESSION_ID_HEADER_NAME. Every time the STICKY_SESSION_TIMEOUT is spent, I set ++counter + JSESSIONID to BALANCER_SESSION_ID_HEADER_NAME.
Client code:
if (USE_STICKY_SESSION_TIMEOUT && this.getjSessionId() != null) {
if (this.getLastResponseTime() != 0
&& new Date().getTime() - this.getLastResponseTime() > STICKY_SESSION_TIMEOUT) {
balancerSubsessionCounter++;
}
final String cookie = BALANCER_SESSION_ID_HEADER_NAME + "=" + balancerSubsessionCounter + "-"
+ this.getjSessionId();
this.addCookie(httpPost, cookie);
}
//invoke service
if (USE_STICKY_SESSION_TIMEOUT) {
this.setLastResponseTime(new Date().getTime());
}
Related
I am trying to use HttpContext.Session in my ASP.NET Core Blazor Server application (as described in this MS Doc, I mean: all correctly set up in startup)
Here is the code part when I try to set a value:
var session = _contextAccessor.HttpContext?.Session;
if (session != null && session.IsAvailable)
{
session.Set(key, data);
await session.CommitAsync();
}
When this code called in Razor component's OnAfterRenderAsync the session.Set throws following exception:
The session cannot be established after the response has started.
I (probably) understand the message, but this renders the Session infrastructure pretty unusable: the application needs to access its state in every phase of the execution...
Question
Should I forget completely the DistributedSession infrastructure, and go for Cookies, or Browser SessionStorage? ...or is there a workaround here still utilizing HttpContext.Session? I would not want to just drop the distributed session infra for a way lower level implementation...
(just for the record: Browser's Session Storage is NOT across tabs, which is a pain)
Blazor is fundamentally incompatible with the concept of traditional server-side sessions, especially in the client-side or WebAssembly hosting model where there is no server-side to begin with. Even in the "server-side" hosting model, though, communication with the server is over websockets. There's only one initial request. Server-side sessions require a cookie which must be sent to the client when the session is established, which means the only point you could do that is on the first load. Afterwards, there's no further requests, and thus no opportunity to establish a session.
The docs give guidance on how to maintain state in a Blazor app. For the closest thing to traditional server-side sessions, you're looking at using the browser's sessionStorage.
Note: I know this answer is a little old, but I use sessions with WebSockets just fine, and I wanted to share my findings.
Answer
I think this Session.Set() error that you're describing is a bug, since Session.Get() works just fine even after the response has started, but Session.Set() doesn't. Regardless, the workaround (or "hack" if you will) includes making a throwaway call to Session.Set() to "prime" the session for future writing. Just find a line of code in your application where you KNOW the response hasn't sent, and insert a throwaway call to Session.Set() there. Then you will be able to make subsequent calls to Session.Set() with no error, including ones after the response has started, inside your OnInitializedAsync() method. You can check if the response is started by checking the property HttpContext.Response.HasStarted.
Try adding this app.Use() snippet into your Startup.cs Configure() method. Try to ensure the line is placed somewhere before app.UseRouting():
...
...
app.UseHttpsRedirection();
app.UseStaticFiles();
//begin Set() hack
app.Use(async delegate (HttpContext Context, Func<Task> Next)
{
//this throwaway session variable will "prime" the Set() method
//to allow it to be called after the response has started
var TempKey = Guid.NewGuid().ToString(); //create a random key
Context.Session.Set(TempKey, Array.Empty<byte>()); //set the throwaway session variable
Context.Session.Remove(TempKey); //remove the throwaway session variable
await Next(); //continue on with the request
});
//end Set() hack
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
...
...
Background Info
The info I can share here is not Blazor specific, but will help you pinpoint what's happening in your setup, as I've come across the same error myself. The error occurs when BOTH of the following criteria are met simultaneously:
Criteria 1. A request is sent to the server with no session cookie, or the included session cookie is invalid/expired.
Criteria 2. The request in Criteria 1 makes a call to Session.Set() after the response has started. In other words, if the property HttpContext.Response.HasStarted is true, and Session.Set() is called, the exception will be thrown.
Important: If Criteria 1 is not met, then calling Session.Set() after the response has started will NOT cause the error.
That is why the error only seems to happen upon first load of a page--it's because often in first loads, there is no session cookie that the server can use (or the one that was provided is invalid or too old), and the server has to spin up a new session data store (I don't know why it has to spin up a new one for Set(), that's why I say I think this is a bug). If the server has to spin up a new session data store, it does so upon the first call to Session.Set(), and new session data stores cannot be spun up after the response has started. On the other hand, if the session cookie provided was a valid one, then no new data store needs to be spun up, and thus you can call Session.Set() anytime you want, including after the response has started.
What you need to do, is make a preliminary call to Session.Set() before the response gets started, so that the session data store gets spun up, and then your call to Session.Set() won't cause the error.
SessionStorege has more space than cookies.
Syncing (two ways!) the sessionStorage is impossible correctly
I think you are thinking that if it is on the browser, how can you access that in C#? Please see some examples. It actually read from the browser and transfers (use) on the server side.
sessionstorage and localstorage in blazor are encrypted. We do not need to do extra for encryption. The same applies for serialization.
I am testing the behaviour of cache and this are my test snippets:
At first, these lines of codes will be executed:
HttpRuntime.Cache.Insert("test", "true",
// No Dependencies
null,
// No absolute expiration (mimic the behavior of forms authentication)
System.Web.Caching.Cache.NoAbsoluteExpiration,
// Timeout 20 minutes after the last access
// (to mimic the behavior of forms authentication)
new TimeSpan(0, 20, 0),
// Setting to NotRemovable ensures that if the
// application pool restarts, you don't lose your cache
System.Web.Caching.CacheItemPriority.NotRemovable,
// No callback needed here
null);
so i have inserted a key and a value to my cache.
I will stop my server, remove that snippet and replace it with
ViewBag.test = "asd" + HttpRuntime.Cache["test"]; to check the previous cache, but it's gone already since it's not displaying anything.
Isn't it meant to be there or this is the normal behavior for it?
I did some researching but i am not sure if this is client side caching. I am kinda new to this and clarifications will be very much appreciated.
The cache is an in-memory cache that resides in your application's memory on the server, and stopping the server stops the program, clearing all its memory. So no, you can expect to never have cached values persist after a restart.
I am creating session variable in rails using session[:name]="sanjay". I have done some research and came to know that session variables in rails are stored as a cookie on client side.so i just want to know that is there any need to delete session variable in cookie based session store.are sessions deleted automatically In server side when client closed the browser??
Thanks,
You don't need to clean up session variables. While I don't think they are automatically deleted on browser terminate they are indeed cleaned up by the browser eventually (expiry dates)
I've implemented sliding sessions in my Relying Party application, as described in Sliding Sessions for WIF 4.5. That works great as far as it goes, but there's one problem that it seems nobody talks about.
As the linked blog post points out, when the RP token expires, the next time make a request the token is re-issued from the STS. Assuming, of course, that the STS session lifetime is longer than the RP's session lifetime, which is almost certainly the case if you're implementing sliding sessions.
In any event, that completely defeats the whole point of sliding sessions.
What nobody seems to talk about is what to do when the RP session expires. What I want is, if the RP session times out (usually because somebody walked away from his desk for 10 minutes), is for my application to redirect to the STS login page where the user can re-authenticate, and then be redirected back to the page I had requested; or perhaps to the page that I was on when I made the request.
I'm almost certain that this is possible, but I have absolutely no idea how it's done.
Here's my code from global.asax:
private const int InactivityTimeout = 5; // minutes
void SessionAuthenticationModule_SessionSecurityTokenReceived
(object sender, SessionSecurityTokenReceivedEventArgs e)
{
var now = DateTime.UtcNow;
var validFrom = e.SessionToken.ValidFrom;
var validTo = e.SessionToken.ValidTo;
double halfSpan = (validTo - validFrom).TotalMinutes/2;
if (validFrom.AddMinutes(halfSpan) < now && now < validTo)
{
// add more time
var sam = sender as SessionAuthenticationModule;
e.SessionToken = sam.CreateSessionSecurityToken(
e.SessionToken.ClaimsPrincipal,
e.SessionToken.Context,
now,
now.AddMinutes(InactivityTimeout),
e.SessionToken.IsPersistent);
e.ReissueCookie = true;
}
else
{
// re-authenticate with STS
}
}
My questions:
Is the else clause the proper place to put the re-authentication logic?
If so, please provide an example, 'cause I have no idea.
If the answer to #1 is no, then is there a separate event I need to subscribe to that will tell me "Hey, your session security token has expired!"?
I'd recommend you sync the session lifetimes on the STS and the RP(s).
You can set the session lifetime to 10 minutes on the STS and 10 minutes on the RP and use the sliding session approach on the RP. After 10 minutes of inactivity both sessions would expire and the user should be required to re-authenticate.
If you have multiple RPs you could implement a form of keep-alive from the RP to the STS - e.g. load a resource from the STS in every webpage on the RPs. Whenever a page is loaded on an RP, the keep-alive resource would be loaded from the STS - refreshing the STS session. After 10 minutes of inactivity they would both time out and the user would have to re-authenticate.
"A resource from the STS" could mean a web page (Web Forms/MVC) loaded in an invisible iframe. The important thing is that it's a managed handler so the request is handled by ASP.NET.
As for your questions, if you sync the session lifetimes so they time out together:
No, you don't need to add any code in the else clause. If the token is expired, WIF will redirect to the STS.
Just remove the else clause.
Let WIF handle this for you.
For completeness, if you can't sync the session lifetimes you could trigger a federated sign-out when the RP session expires. The following snippet triggers a signout at the configured Issuer (STS). You could put this in the else clause to trigger a signout on the first request after the RP session expires:
using System.IdentityModel.Services; //WIF 4.5
var stsAddress = new Uri(FederatedAuthentication.FederationConfiguration.WsFederationConfiguration.Issuer);
WSFederationAuthenticationModule.FederatedSignOut(stsAddress, null); //Optional replyUrl set to null
Hope that helps!
I have a tomcat server on which I have deployed 10 web applications.
I have a Filter which reads an incoming request from one application (say "A"), reads a certain cookie value, and gets another context name (Say "B") from the database based on the value of the cookie.
This filter then rewrites the request with context A(e.g. http://1.1.1.1/A/XYZServlet) to a new request having context as B (e.g. http://1.1.1.1/B/XYZServlet).
On doing this, all the session variables are lost when the new request gets executed.
How can I make sure that the session and all the session variables(related to context A) are retained when my filter redirects the old request(context A) to the new request(context B) ?
Setting emptySessionPath = "true" in server.xml cannot be a solution due to certain architecture reasons.
Tomcat sessions are scoped to a single web application. In your case, when you switch to a different context, the session data is not lost, but simply not applicable to the new context. The scoping is controlled by the session cookie's path, it will contain the context of the creating web application and thus be only sent to the Tomcat for requests within that context.
If you want to extend the session's scope, several options come to mind: You could modify the session cookie (JSESSIONID by default) to the root context path /. Yet session cookie management is very deeply embedded in Tomcat and hard (if not impossible) to modify. I once tried to change the session cookie to be persistent with a fixed validity duration, but didn't find a way to do so.
A less elegant but maybe working way would be to duplicate the session cookie. I'm not sure if it will work, but in your filter, you could read the session cookie and create a duplicate with the target context as path. On subsequent requests, the faked session cookie would be included, possibly making your previous session data available.