i search google and found many answers for detecting session expiry programmatically. this is code which i saw everyone use it
global.asax
---------------
protected void Session_Start(object src, EventArgs e)
{
if (Context.Session != null && Context.Session.IsNewSession)
{
string sCookieHeader = Request.Headers["Cookie"];
if (null != sCookieHeader && sCookieHeader.IndexOf("ASP.NET_SessionId") >= 0)
Response.Redirect("/Session/Timeout");
}
}
i have few question on the above code
1) when session expire then how Context.Session will not be null?
2) what is the meaning of this line Request.Headers["Cookie"];
looking for good discussion. thanks
You can use the Session_End method in global.asax file
void Session_End(Object sender, EventArgs E) {
// Clean up session resources
}
1) when session expire then how Context.Session will not be null?
Your code triggers when a user comes back to the server with an expired session.
The session object in this code is the new empty session which is beginning.
Remember you are in Session_Start, not in Session_End.
2) what is the meaning of this line Request.Headers["Cookie"];
The cookie contains the session's id. If a user is requesting a ressource, providing a session id, and Session_start is triggered, that almost certainly means that the session id refers to an expired session object. A specific message is then displayed to the user.
Related
I have a relatively simple WebForms-based site using Forms Authentication:
<authentication mode="Forms">
<forms loginUrl="login.aspx" defaultUrl="secure/home.aspx" name=".AdminSite" />
</authentication>
As it's not explicitly mentioned, slidingExpiration is set to true by default, and thus a user is not logged off as long as they're still navigating around the site.
However, I'd like a specific page to not increment the expiry time. Is this possible, either within web.config or in code? The only suggestions I've seen mention setting slidingExpiration to false, which would apply side-wide.
The authentication cookie is set using:
FormsAuthentication.RedirectFromLoginPage(username, False)
and therefore altering the authentication cookie itself isn't practical.
The sliding expiration is achieved by the FormsAuthentication module by re-issuing the cookie when necessary. To prevent the sliding, you need to prevent the cookie renewal from happening.
This can be done by simply removing the FormsAuthentication cookie from the response.
Below is the code behind from a very simple web form. The aspx page has a div that shows the output from the Page_Load event.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
testDiv.InnerHtml = "Hi, cookie is: " + HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName].Value;
testDiv.InnerHtml += "<br />";
var ticket = FormsAuthentication.Decrypt( HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName].Value);
testDiv.InnerHtml += "Expires: " + ticket.Expiration.ToString("yyyy-MM-dd HH:mm:ss");
if(Response.Cookies.AllKeys.Contains(FormsAuthentication.FormsCookieName))
testDiv.InnerHtml += "<br />Forms auth is trying to update the cookie in this response";
}
protected void Page_Prerender(object sender, EventArgs e)
{
if (Response.Cookies.AllKeys.Contains(FormsAuthentication.FormsCookieName))
Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
}
}
The Page_Prerender event removes the FormsAuthentication cookie from the response if it's present, thereby preventing the sliding.
I tested this by setting the timeout for the FormsAuthentication to two minutes. Then I start debug and log in. Then I keep refreshing the page in question.
Since FormsAuthentication doesn't update the cookie unless half the expiration time has gone, what happens is that for the first minute, the page will keep showing the same encrypted cookie and the same expires time. After a bit more than one minute, the page will be reporting that FormsAuthentication is trying to renew the cookie. But the Page_Prerender removes the cookie so it doesn't get sent. After another minute you will be redirected to the login page.
Testing the same but removing the Page_Prerender method show that the cookie is changed and the expires time updated after about one minute.
You could set the response cookie's expiry date to the expiry date of the request cookie, effectively overwriting what the system is doing for that specific page.
After some thought, I deviated from trying to alter the cookie or create a second cookie or override the cookie by changing the Session.Timeout. I think it may actually be easier to use a timer, using System.Timers. The methods for the timer can always be put into a separate class if you like.
using System.Timers;
public partial class MyPage:Page
{
private System.Timers.Timer timer;
protected void Page_Load(object sender, EventArgs e)
{
SetTimer();
}
private void SetTimer()
{
// Interval is set in milliseconds- set as you please.
timer = new System.Timers.Timer(1000 * 60);
timer.Elapsed += OnTimedEvent;
timer.AutoReset = true;
timer.Enabled = true;
}
// In this handler, stop the timer and call a method to clear all cookies.
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
timer.Stop();
ClearAllCookies();
}
// Method to clear all cookies. There may be a simpler way to do this, you are vague about your cookies, so I supplied a clear all.
public void ClearAllCookies()
{
HttpCookie cookie;
string cookieName;
int cookieCnt = Request.Cookies.Count;
for(int i = 0; i < cookieCnt; i++)
{
cookieName = Request.Cookies[i].Name;
cookie = new HttpCookie(cookieName);
// This causes the cookie to expire
cookie.Expires = DateTime.Now.AddDays(-1);
Response.Cookies.Add(cookie);
}
Response.Redirect("LogIn.aspx");
}
}
Edit
Or use a method that logs the user out. Either way, you will end the session without having to fiddle with the user's authentication for the duration of the remainder of the website, except to end it if the session times out on this particular page.
public void ForceLogOff(){
Session.Clear();
Session.Abandon();
Session.RemoveAll();
// Do here whatever you need to do.
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
Response.Redirect("LogIn.aspx");
}
How you end the session is up to you. This provides you a way to override the sliding expiration issue, and set a custom timeout from within one page only.
I'm using VS2010 and created a simple asp. web forms application, using Development Server to test it.
I try to store user data - queried from sql server - in the session, since I don't want to access database in every request. I'm using the 'Application_AuthenticateRequest' and the 'Session_Start' methods.
First round:
AuthenticateRequest called. The following code ran:
public static void Initialize(string login_name, bool force_refresh)
{
HttpSessionState Session = HttpContext.Current.Session;
object o = Session == null ? null : Session["EMPLOYEE_DATA"];
if (force_refresh || o == null || o.GetType() != typeof(Employee) || (o as Employee).login_name!= login_name)
{
_current = UIManager.GetEmployee(login_name);
if (Session != null)
{
Session["EMPLOYEE_DATA"] = _current;
}
}
else
{
_current = (Employee)o;
}
}
The _current variable is a private static field published through a static property.
In the first round the Session is null, and I think it's ok because the Session_Start not called yet.
The Session_Start looks like this:
protected void Session_Start(object sender, EventArgs e)
{
Session["EMPLOYEE_DATA"] = EmployeeFactory.Current;
}
In the next round the Session_Start is not called of course but in the AuthenticateRequest I can't access to the session. The HttpContext.Current.Session is null and the this.Session reference throw a HttpException says the "Session state is not available in this context".
However I can access the Session from any of the page_load events but it's a bad practice I think that I put authentication every page_load.
Any idea how can I access to the Session?
Thanks for advice,
Péter
You're not able to use Session on the Application_AuthenticateRequest becauase it's not bound at that moment.
I think you're able to use the event Application_AcquireRequestState.
try to use the below code in page_Load
Response.AppendHeader("Refresh", Convert.ToString(Session.Timeout * 15) + ";
URL=SessionExpPage.aspx");
This is my base class for all pages except EndSession.aspx
override protected void OnInit(EventArgs e) {
base.OnInit(e);
if (Context.Session != null)
{
//check the IsNewSession value, this will tell us if the session has been reset.
//IsNewSession will also let us know if the users session has timed out
if (Session.IsNewSession)
{
//now we know it's a new session, so we check to see if a cookie is present
string cookie = Request.Headers["Cookie"];
//now we determine if there is a cookie does it contains what we're looking for
if ((null != cookie) && (cookie.IndexOf("ASP.NET_SessionId") >= 0) )//&& !Request.QueryString["timeout"].ToString().Equals("yes"))
{
//since it's a new session but a ASP.Net cookie exist we know
//the session has expired so we need to redirect them
Response.Redirect("EndSession.aspx?timeout=yes");
}
}
}
}
But on EndSession I try to navigate back to, say default.aspx, and then this code above just redirects be back to EndSession.aspx.
So for better clarification:
Step 1: Go to mypage.aspx
Step 2: Wait for timeout
Step 3: try to navigate away
Step 4: get redirected to EndSession.aspx
Step 5: try to navigate away
Step 6: GoTo set 4
Setp 6 should be actually being able to navigate away...
(if needed pelase ask for further clarification)
Any ideas?
THANKS!!!
I got rid of the basepage that I had originally.
Put this in the Session_Start of Global.asax
void Session_Start(object sender, EventArgs e)
{
string cookie = Request.Headers["Cookie"];
// Code that runs when a new session is started
if ((null != cookie) && (cookie.IndexOf("ASP.NET_SessionId") >= 0))//&& !Request.QueryString["timeout"].ToString().Equals("yes"))
{
if(Request.QueryString["timeout"] == null || !Request.QueryString["timeout"].ToString().Equals("yes"))
Response.Redirect("Default.aspx?timeout=yes");
}
}
Put this on the Defualt.aspx page:
if (!IsPostBack)
{
if (Request.QueryString["timeout"] != null && Request.QueryString["timeout"].ToString().Equals("yes"))
{
Response.Write("<script>" +
"alert('Your Session has Timedout due to Inactivity');" +
"location.href='Default.aspx';" +
"</script>");
}
}
This solution works even when the timeout occurs on the Default.aspx page
The discusion for the solution I used is posted here: How to stop basepage from recursivly detecting session timeout
I'm recording the session start times from when people log into my .NET 2.0 web application, but I'd also like to record the Session ID. Can someone give me some example code on how to accomplish this (how to access the Session ID from within the Global.ASAX).
If you need any additional info just let me know.
HttpContext.Current.Session.SessionID
Edit to show null test:
if ((HttpContext.Current != null) && (HttpContext.Current.Session != null) {
id = HttpContext.Current.Session.SessionID
}
You can get at it quite simply with HttpContext.Current.Session.SessionId as you probably already know. You need to be on or after Application_AcquireRequestState before the session state has been loaded, and session state is also only loaded when the requested resource implements IRequiresSessionState. You can see a list of all the events in global.asax here: https://web.archive.org/web/1/http://articles.techrepublic%2ecom%2ecom/5100-10878_11-5771721.html and read more about IRequiresSessionState here: http://msdn.microsoft.com/en-us/library/system.web.sessionstate.irequiressessionstate.aspx
Write to the session the datetime and sessionid at the moment of the first request following ASP.NET's identifying the user's session.
protected void Application_PreRequestHandlerExecute(object sender, EventArgs eventArgs) {
var session = HttpContext.Current.Session;
if (session != null) {
if (session["foo"] == null) {
session["foo"] = DateTime.Now.Ticks + "|" + session.SessionID;
}
}
}
Can anybody help me to find out solution of following problem.
In ASP.NET website: at Application_OnPostAuthenticate() event, whatever code i write is executed for every request. therefore due to this customidentity object, countryid and weatherid is called everytime for each request (call for database for value). It effect response time of page and unneccessary code execute.
void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
// Get a reference to the current User
IPrincipal objIPrincipal = HttpContext.Current.User;
// If we are dealing with an authenticated forms authentication request
if ((objIPrincipal.Identity.IsAuthenticated) && (objIPrincipal.Identity.AuthenticationType == "Forms"))
{
CustomPrincipal objCustomPrincipal = new CustomPrincipal();
objCustomPrincipal = objCustomPrincipal.GetCustomPrincipalObject(objIPrincipal.Identity.Name);
HttpContext.Current.User = objCustomPrincipal;
CustomIdentity ci = (CustomIdentity)objCustomPrincipal.Identity;
HttpContext.Current.Cache["CountryID"] = FatchMasterInfo.GetCountryID(ci.CultureId);
HttpContext.Current.Cache["WeatherLocationID"] = FatchMasterInfo.GetWeatherLocationId(ci.UserId);
Thread.CurrentPrincipal = objCustomPrincipal;
}
}
To solve this problem when i try tochange code as follows
HttpContext.Current.Session.Add("test", FatchMasterInfo.GetWeatherLocationId(ci.UserId);); in place of cache i found foolowing error
"Object refrence not set to the instance of object"
I don't know whether we can store session variable inside Application_OnPostAuthenticate() event or not?
You could try doing this a bit later in the request, such as in the PreRequestHandlerExecute event:
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
IPrincipal objIPrincipal = HttpContext.Current.User;
if ((objIPrincipal.Identity.IsAuthenticated) && (objIPrincipal.Identity.AuthenticationType == "Forms"))
{
HttpSessionState session = HttpContext.Current.Session;
CustomPrincipal objCustomPrincipal = new CustomPrincipal();
if (session[objIPrincipal.Identity.Name] == null)
{
// get data from database or wherever
objCustomPrincipal = objCustomPrincipal.GetCustomPrincipalObject(objIPrincipal.Identity.Name);
CustomIdentity ci = (CustomIdentity)objCustomPrincipal.Identity;
Object countryID = FatchMasterInfo.GetCountryID(ci.CultureId);
Object weatherLocationID = FatchMasterInfo.GetWeatherLocationId(ci.UserId);
// save in session (not cache as cache is application-wide, not per-user):
session.Add(objIPrincipal.Identity.Name, objCustomPrincipal);
session.Add(objIPrincipal.Identity.Name + "_CountryID", countryID);
session.Add(objIPrincipal.Identity.Name + "_WeatherLocationID", weatherLocationID);
}
else
{
// already have custom principal object in session
objCustomPrincipal = (CustomPrincipal)session[objIPrincipal.Identity.Name];
}
// set the custom principal object to context/thread
HttpContext.Current.User = objCustomPrincipal;
Thread.CurrentPrincipal = objCustomPrincipal;
}
}
You probably don't want to access the session in any event that happens in every request. Some requests don't even have session (for instance, a lot of web service calls, or calls to WebResource.axd that load static resources).
Before adding value to cache object, check if it already exists in the cache.
You might not have session state enabled. Does it work anywhere else (like in a web form's display)?
Look for a <sessionState> element under your system.web element in web.config make sure it's turned on (set it to InProc unless you have a web farm).