sharing session data between domains in asp.net, confused! - asp.net

Ok, here's my problem, i want to maintain session data between two applications or domains (eg:- www.abc.com and secure.abc.com).
I have read on net about this, but many people pointing many different ways to do it, with people commenting +ve and -ve responses to all. Plus many are just providing theoretical answer, do this and that ,but no code at all.
are these steps all that is required?
1) in web.config: <httpCookies domain=".abc.com"/>
2) store session data in sql DB as:(after preparing the db for storing sessions)
<sessionState mode="SQLServer" sqlConnectionString="Data Source=YourServer;
Integrated Security=True;database=MySessionDB" sqlCommandTimeout="30"
allowCustomSqlDatabase="true"/>
<machineKey decryption="AES" validation="SHA1" decryptionKey="..." validationKey="..." />
3)Am confused about this one: i want to set the domain for the session cookie like this
Response.Cookies["ASP.NET_SessionId"].Domain = ".abc.com";
But where should this code be written?
this entry: http://mgrzyb.blogspot.com/2007/12/aspnet-and-subdomains.html says: use System.Web.SessionState.SessionIDManager as a base class but the SaveSessionID method is not virtual so cannot be overridden. Options are: either explicitly re-implement the interface method or decorate SessionIDManager class and after calling SessionIDManager.SaveSessionID set Response.Cookies[SessionIdCookieName].Domain to our domain.
Only if the author had provided real code, step 3 would have been clear.
Can anyone provide the code for it.
Plus all this 3 steps enough to share session among the domains?

the 3rd step statement can be written in global.asax according to: http://www.know24.net/blog/ASPNET+Session+State+Cookies+And+Subdomains.aspx
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
/// only apply session cookie persistence to requests requiring session information
#region session cookie
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState )
{
/// Ensure ASP.NET Session Cookies are accessible throughout the subdomains.
if (Request.Cookies["ASP.NET_SessionId"] != null && Session != null && Session.SessionID != null)
{
Response.Cookies["ASP.NET_SessionId"].Value = Session.SessionID;
Response.Cookies["ASP.NET_SessionId"].Domain = ".abc.com"; // the full stop prefix denotes all sub domains
Response.Cookies["ASP.NET_SessionId"].Path = "/"; //default session cookie path root
}
}
#endregion
}

Related

Overridden GetVaryByCustomString method being called only on first request to a page

I have the following question regarding outputcache work in either ASP.Net or Composite C1 CMS (don't know where's the problem):
I need to vary cache by cookie named "City". In Global.asax I've overridden the GetVaryByCustomString method.
public override string GetVaryByCustomString(HttpContext context, string custom)
{
var result = ApplicationLevelEventHandlers.GetVaryByCustomString(context, custom);
if (result == null) {
return base.GetVaryByCustomString(context, custom);
}
HttpCookie cookie = context.Request.Cookies["City"];
string additionalVary = "";
if (cookie != null)
additionalVary = cookie.Value;
else
additionalVary = "Москва";
return String.Concat(result, additionalVary);
}
And the problem is: the breakpoint of my overridden GetVaryByCustomString method is being hit only on the first request to a page, but isn't being hit on next requests until the cache actually expires (60 seconds in my case).
While the cookie might have been changed on the client side during that 60 seconds - the page is simply taken from the cache.
Where am I wrong?
Thanks in advance.
UPDATE:
I was finally able to make outputcache vary by cookie by the following modifications in the CacheProfile settings:
<outputCacheProfiles>
<add name="C1Page" duration="60" varyByCustom="C1Page" varyByParam="*" varyByHeader="Cookie" location="Server" />
</outputCacheProfiles>
In this case the method GetVaryByCustomString is being hit on each request, but there's no need in it: these settings make the cache vary by every cookie might be set on the website. Of course it is not a desired behavior - that's why I'm continuing the investigation.
What is even more funny - these 2 parameters (varyByHeader="Cookie" and location="Server") work only together, disabling one of them make the things look exactly as before - outputcache not varying.
Try to put a break point to Application_BeginRequest, it could be that browser isn't making a request but rather showing a cached page.

Export IIS in-process session data while debugging

I have production IIS server with some ASP.NET MVC app. I got some tricky bug which I can't capture. It's linked to session data. How can I export/see/view such user session? There is default IIS configuration for session storing -- in-process.
EDIT
By the way I have necessary appropriate user session ID.
EDIT2
Ok, guys, so even if I can't export that data right now, could you please point me at some session state server or something similar, which I can use for storing session data and view it further?
I kniw SQL Server can, but it is very heavy for such issue.
Chris is right following on his Idea, you could write a routine that would output the content of your session objects to a file (a kind of a custom log).
//Controller Action where you store some objects in session
public ActionResult Index()
{
var myObj = new { strTest = "test string", dtTestValue = DateTime.Now, listTest = new List<string>() { "list item 1", "list item 2", "list item 3" }};
Session["test1"] = "Test";
Session["test2"] = myObj;
return View();
}
//Controller Action where you output session objects to a file
[HttpPost]
public ActionResult Index(FormCollection form)
{
//Routine to write each sessionObject serialized as json to a file
foreach (string key in Session.Keys)
{
var obj = Session[key];
JavaScriptSerializer serializer = new JavaScriptSerializer();
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#"C:\Users\Public\CustomAspNetLog.txt", true))
{
file.WriteLine(DateTime.Now.ToString() + "\t" + serializer.Serialize(obj));
}
}
return View();
}
If you need to call that routine often, you can put it in some helper class and call it whenever you want in your controller actions. Then you are able to inspect true data inside Session at every step you find necessary.
No, you will need to write a routine to export the session data as and when required.
KSeen
There is a better option to store sessions than a StateServer i.e Distributed Cache provider.
Alachisoft provides NCache Express which is totally free.You can use it to store your sessions.Here is how you do it.
Install NCache on each web server.
Define a distributed cache: Make sure you test the distributed cache to ensure it is properly working.
Modify web.config file: to add the SessionState Provider information and the name of the cache you've just created.
<sessionState cookieless="false" regenerateExpiredSessionId="true"
mode="Custom"
customProvider="NCacheSessionProvider" timeout="1">
<providers>
<add name="NCacheSessionProvider"
type="Alachisoft.NCache.Web.SessionState.
NSessionStoreProvider"
cacheName="myreplicatedcache"
writeExceptionsToEventLog="false"
AsyncSession="false"/>
</providers>
</sessionState>
Please note that Version=3.2.1.0 should match the specific NCache Express version you've downloaded. Once you do this, you're ASP.NET application is ready to start using distributed sessions.

Enable roles without (or with a dummy) Role Provider

I'm following this article in which is described how to assign roles to users when theiy log-in using forms authentication:
public void Application_AuthenticateRequest( Object src , EventArgs e )
{
if (!(HttpContext.Current.User == null))
{
if (HttpContext.Current.User.Identity.AuthenticationType == "Forms" )
{
System.Web.Security.FormsIdentity id;
id = (System.Web.Security.FormsIdentity)HttpContext.Current.User.Identity;
String[] myRoles = new String[2];
myRoles[0] = "Manager";
myRoles[1] = "Admin";
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(id,myRoles);
}
}
}
I put the role logic in the event handler, so I basically don't need a role provider. Nonetheless, in order to run this, appears that I must enable Role Provider in web.config. Sadly, if I just put:
<roleManager enabled="true"/>
it results in runtime errors related to a failed connection to the SQL server, like if I chose AspNetSqlRoleProvider as Role Provider.
What should I do to have roles working this way? How can I choose to use no role provider, or how should I implement a dummy one (if it makes any sense)?
You shouldn't need to enable roleManager in web.config - after all, people used to use roles with .NET 1.x before roleManager came along.
One thing that roleManager will do for you that you haven't done in your code is set Thread.CurrentPrincipal to HttpContext.Current.User. If you're relying on this (e.g. using PrincipalPermissionAttribute), then you need to add this:
Thread.CurrentPrincipal = HttpContext.Current.User;
Otherwise, I'd expect it to work: what symptoms are you seeing that makes you think it isn't working?
As for implementing a dummy RoleProvider, it's easy enough: for example see this MSDN article.
You only need to implement the GetRolesForUser and IsInRole methods; the other methods can simply throw NotSupportedException.

The Application_PreRequestHandlerExecute event doesn't fire for PageMethods. What can I use instead?

This article explains that the PreRequestHandlerExecute event does not fire for PageMethod calls for whatever reason. However, I'm trying to use that event to populate the Principal object with the user's permissions so they can be checked within any web request (PageMethod call or not). I'm caching the permissions in the Session, so I need an event that fires whenever a PageMethod is called, and I need to have access to the Session. This way I can populate the Principal object with the security permissions cached in the session, and User.IsInRole() calls will work as expected. What event can I use?
You should implement an authorization module that will be run with every request that goes up to the server. This way you are able to authorize your principal for any request that come up to the server (page request, method, etc.)
public class AuthorizationModule : IHttpModule, IRequiresSessionState
{
//not going to implement it fully, might not compile
public void Init( HttpApplication context )
{
//you'll prolly want to hook up to the acquire request state event, but read up to make sure this is the one you want on the msdn
context.AcquireRequestState += AuthorizeRequest;
}
public void AuthorizeRequest( HttpContextBase httpContext )
{
// do you work in here
// you can redirect them wherever if they don't have permssion, log them out, etc
}
}
}
After you've crated the module, you'll need to hook it up in the web.config. Your type should include the namespace if it has one.
<httpModules>
<add name="AuthorizationModule" type="AuthorizationModule"/>
</httpModules>
I hope this helps.
You can use the Application_OnPostAuthenticateRequest as shown below (assuming you are using Forms Authentication. Else, pls replace the code with your Authentication mechanism):
public void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
IPrincipal usr = HttpContext.Current.User;
if (usr.Identity.IsAuthenticated && usr.Identity.AuthenticationType == "Forms")
{
var fIdent = (FormsIdentity)usr.Identity;
var ci = new CustomIdentity(fIdent.Ticket);
var p = new CustomPrincipal(ci);
HttpContext.Current.User = p;
Thread.CurrentPrincipal = p;
}
}
Page Methods are static, and bypass the normal Page lifecycle, its objects and its events. The best you can do is pass authentication information as parameters to the Page Method itself.
From my point of view, you can:
1.- Use a common method you can call from every page method server code that have access to Session variables. Please refer to:
http://mattberseth.com/blog/2007/06/aspnet_ajax_use_pagemethods_pr.html
2.- Try to capture a similar behaviour later using __doPostBack() function to run server code. See if this work for you to capture page method async posbacks:
http://www.dotnetcurry.com/ShowArticle.aspx?ID=256
Hope that helps,

Refresh ASP.NET Role Provider

simple question...
Given I have an ASP.NET site, which uses a [custom] RoleProvider,
Is there any way in which I can somehow "refresh" the provider without forcing the user to log out of the site and log back in?
I'm looking for something that would be akin to a fictional method
Roles.Refresh()
Specifically, I am looking at this for if an administrator changes a user's roles, the user sessions could maybe refresh themselves every 10 minutes or something.
I assume you have something like this in your web.config:
<roleManager enabled="true" defaultProvider="..."
cacheRolesInCookie="true">
The roles are cached in a cookie , so you can force them to refresh by deleting the cookie. This method worked for me. I added the cookieName attribute so that I don't rely on asp.net's default. For your scenario, though, you may be able to just set the cookieTimeout attribute to something reasonable and be done with it.
This method won't update the roles immediately, of course. They will be updated on the next page load after you delete the cookie.
Refresh just need to delete the cookie:
For C#: Roles.DeleteCookie(); // Works as Roles.Refresh()
If you don't want to use cookies you can use Session object to cache the roles.
like this:
public override string[] GetRolesForUser(string username)
{
System.Web.SessionState.HttpSessionState Session = HttpContext.Current.Session;
if (Session["roles"] == null)
Session["roles"] = MyDataProvider.Security.GetRolesForUser(username);
return (string[])Session["roles"];
}
When you need to update the roles for this user you can do
Session["roles"] = null
depend on the custom role provider used.
Just call a "update my role" function on every request? (bad way but at least your sure to update it)
The roles are cached in a cookie (encrypted of course). The simplest solution will be to disable caching in the web.config file. You will loose some performance.
Else you must somehow resend the auth cookie. One major problem is that many browsers will not accept cookies on redirects with method post.
The other solution that worked for me:
1) In a aspx methodod log the user out and store the username in the session
//Add User to role reviewer and refresh ticket
Roles.AddUserToRole(User.Identity.Name, Constants.ROLE_REVISOR);
FormsAuthentication.SignOut();
FormsAuthentication.SetAuthCookie(User.Identity.Name, false); //Might work in some browsers
Session["REFRESHROLES"] = User.Identity.Name;
Response.Redirect("someprotectedurl?someid=" + someid);
2) In the loginpage sign the user in again if username is stored in session
protected void Page_Load(object sender, EventArgs e)
{
string returnUrl = Request.QueryString["ReturnUrl"];
if(String.IsNullOrEmpty(returnUrl) == false)
{
if(Session["REFRESHROLES"] != null)
{
if(!string.IsNullOrEmpty(Session["REFRESHROLES"].ToString()))
{
FormsAuthentication.SetAuthCookie(Session["REFRESHROLES"].ToString(), false);
Session.Remove("REFRESHROLES");
Response.Redirect(returnUrl);
return;
}
}

Resources