I have some variable in my DataEntityLayer which are common for all the users for example
public class DataEntityLayer : System.Web.UI.Page
{
public string UserName
{
get
{
if (Session["UserName"] == null)
{
GotoLoginPage();
return null;
}
else
return Session["UserName"].ToString();
}
set { Session["UserName"] = value; }
}
}
I am using this variable to display Username when a user logs in, this is working fine but goes null frequently. How can I avoid that or let me know a way to declare a variable in my Layer to show Username of a user when he logs in.
Session is temporary memory that expires after a defined amount of time, I believe the default is 20 minutes of inactivity.
It could also be because you're using multiple servers with load balancing. Unless the session is persisted in a database then as you move from one server to another, the session is lost.
I would recommend avoiding this approach for the above reasons. There are better and more secure ways to handle authentication.
P.S An object called DataEntityLayer should not derive from System.Web.UI.Page!!
I totally agree with #johnMc in regards to the security, best use a library or package that gives a total solution. For completeness, the reason for the session being lost could be also running the service on a multi-threaded application. If using IIS, you can turn on a service called "ASP.Net state service" and then enable it in your web config. This does out-of-process session handling for ASP.net. There is also a way to connect to this service using non-IIS, but you'll have to build a communicator class to handle this. There is a small performance hit for this process, but I've never had any issues on high loads.
<system.web>
<sessionState mode="StateServer" />
</system.web>
(I would've commented, but I can't add a comment yet?) :)
Related
I have written a webservice that basically inserts data into the central database and is expected to be consumed by multiple clients from multiple systems.
It works but the problem is that it can be accessed by anyone hence making it vulnerable to be used by anybody i.e. anybody can spam by entering data or anything.
One way is to check for the Session variable but how would I know the name of the session variable of the client consuming the system or may be he's not authenticating that way?
So what should I do to make it secure?
[WebMethod(EnableSession= true)]
public int InsertEngineeringData(string FunctionalLocation, string EqptType, string WINFileNo, string ComponentTagNo)
{
try
{
if (Session["User"] != null)
{
}
int EngineeringDataID = 0;
EngineeringDataDAL EngineeringDataDAL = new Vail_PlantWebApi.EngineeringDataDAL();
EngineeringDataID = EngineeringDataDAL.InsertEngineeringData(FunctionalLocation, EqptType, WINFileNo, ComponentTagNo);
return EngineeringDataID;
}
catch (Exception ex)
{
throw ex;
}
}
If it is an asmx webservice, then use the link Crocoder posted or another quick way if it works is you can try the [Authorize] attribute although I'm not sure if that will work with an inline webmethod you're using, I've only seen it used in WebAPI. Authorize attribute in ASP.NET MVC
A more robust way that would definitely work is you add a column to the Users table called 'CurrentSessionID' and another one that says 'LastLoginDateStamp' For each login request if you have a valid user you update their session there and a datestamp. Then when the user hits the api, you compare the session and make sure it hasn't exceeded what you decide is a valid threshold for the last login, maybe 24 hours for example.
There would be a lot more work to do after that, but that's the basic idea.
I'm currently having some configuration variables in my Web.config, eg:
<appSettings>
<add key="RecipientEmailForNotifications" value="asdf#sdf.com" />
<add key="NotifyNewEntries" value="true" />
<!-- etc... -->
</appSettings>
I have a view where admin users can change this values online, eg.
/Admin/Configuration/
Recipient email for notifications:
[___________]
Notify new entries:
[x] Yes
[SAVE]
But there are two problems with this approach:
Each time I deploy the web.config, I'm overriding those values that were saved online
Each time the web application restarts (because web applications are naturally stopped when there is no activity), these values that were saved online are reset to the originals of the last deployment
So the question is:
What is the best way of achieving this goal of having configuration variables that have to be persisted?
I think that using a table isn't very natural, but I might be wrong (please explain why).
Also, using app settings is very convenient when reading the values:
ConfigurationManager.AppSettings["RecipientEmailForNotifications"]
As opposed of using the database,
db.SomeConfigurationTable.SingleOrDefault(.....)
I'd use a combination of database store and caching.
If you are on a single web server you could store the settings in proc cache. If you are in a web farm you could look at something like couchbase (free memcached) or azure.
It might make sense to warm your cache on applicaiton start. That is read the settings from your database and populate your cache.
Another popular approach is to have a Get call that check the cache first and populate cache if cache is empty, e.g.
public string Get(string key, Func<string> getItemCallback)
{
var item = Cache.Get(key) as string;
if (item == null)
{
item = db.SomeConfigurationTable.SingleOrDefault(key);
Cache.Store(key, item);
}
return item;
}
Not forgetting you will need to burst your cache when updating settings.
I have followed a tutorial to implement LDAP (Active Directory) authentication to an ASP.NET/C# 4.0 Web Application. I have the authentication working, and am able to log in under a user of our domain. The next step however is not covered in this tutorial, where I need to keep a session-specific object with some variable data.
Now that I have LDAP authentication working, I'm making a class to wrap the session. However, I'm not sure how I can create this session in a way that it will stay active through all this user's requests. In Globals.asax, I have utilized Application_AuthenticateRequest as required in the tutorial. I'm assuming there's something I need to do here, but since I'm new to C# (more familiar with Delphi), I don't know where I need to actually declare/create this user class instance.
This class contains some things I'd like to keep accessible throughout this user's session, assuming of course the server will stay running throughout this time. For example, a dataset containing product data, which the user may request various parts of this same dataset in different requests. Therefore, it must stay accessible throughout the entire user's session, not just that single HTTP request.
I think you should have a look at Forms authentication for asp.net.
Web.config:
<authentication mode="Forms">
<forms name=".ASPXFORMSAUTH" loginUrl="/Login.aspx" timeout="120" />
</authentication>
<authorization>
<deny users="?" />
</authorization>
After you have managed to log in your visitor using ldap you can log in that visitor in your web-application with very little code:
PrincipalInfo.CurrentPrincipal = PrincipalInfo.CreatePrincipal(userName);
FormsAuthentication.SetAuthCookie(userName, true);
I would also give you an advice about using Session. Jeffery gives you a simple example of how to use the Session object. But don't go store multiple single values in Session; instead create a class like 'VisitorInformation' with all properties you will need. And then make a static manager that sets and gets that visitor information.
Visitor visitor = VisitorManager.CurrentVisitor;
string name = visitor.Name;
int age = visitor.Age;
private const visitorSessionKey = "visitorSessionkey";
public static Visitor CurrentVisitor
{
{
get { return (Visitor)Session[visitorSessionKey] ?? new Visitor(); }
}
}
This way you won't sprinkle your code with calls to session all over the place, with the increasing risk of spelling a key wrong or getting values out of sync.
You can read or write to Session almost anywhere in asp.net.
Examples:
using System.Web;
...
Session["UserAge"] = 28;
var userAge = (int)Session["UserAge"];
Session does have a timeout period, which may be configured in the web.config. As long as requests are being made to the browser, the session information should persist.
See MSDN for more info: http://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.aspx
There is a built-in object called Session that allows you to add and retrieve things from the Session. They are looked up automatically via a session cookie that is maintained by ASP.NET.
I am building an ASP.NET UI on an existing system, which consists of separate SQL server databases for each project. An "enterprise" database lists all current projects which allows anonymous users to select the project to work in. The project name is stored in a session variable. When log in is required the username/password/roles etc are obtained from the database indicated by the project name. I have implemented my own basic membership and role providers to do this, with changes in web.config to specify the roles required for specific pages. (I do not use the standard ASP.NET Configuration tool to manage users, I have existing apps that work with my user tables).
This all seemed to work initially but I discovered that the session variables are not yet loaded at the time when the authorization system checks the roles the current user belongs to in order to determine if the page is accessible. So if we have a < allow roles="xxx" > in web.config then the authorization system fires before session data is loaded and thus before I know which project database should be used.
[Specifically: HttpContext.Current.Session is null when the call to RoleProvider.GetRolesForUser is made]
Anybody who has tackled this problem should know exactly what I'm talking about. My questions therefore are:
A) What is the "Best Practise" solution to this scenario?
B) Could I be storing the project name somewhere else (not in session variable) that is available during the authorization phase?
[Update: Yes - we can use cookies, assuming we do not require cookieless operation]
C) Is there a way to manually get the session variable at this earlier time?
I tried an option to cache roles in cookies, but after a few minutes of testing with that option on I found GetRolesForUsers was still being called.
Thanks
Update:
Here is another description of the root problem which suggests "The application could cache this information in the Cache or Application objects.":
http://connect.microsoft.com/VisualStudio/feedback/details/104452/session-is-null-in-call-to-getrolesforuser
Update:
This looks like the same problem found here:
Extending the RoleProvider GetRolesForUser()
Update:
There was a suggestion about using UserData in FormsAuthenticationTicket, but I require this data even when not logged on.
UPDATE: I solve this these days in a much simpler way by using a wildcard SSL certificate that allows me to configure subdomains for each project, thus the project selection is specified directly in the URL (and each project gets its own subdomain). I still use a cookie hack purely for testing purposes when running on localhost where we have no subdomains.
Original solution:
I have not found any "best practise" write up on this scenario, but here is what I have settled on:
1) In order to support anonymous users switching between projects (i.e. SQL databases) I simply use a session variable to track the project selection. I have a global property that uses this project selection to serve the corresponding SQL connection string as and when it is required.
2) In order to support the call to GetRolesForUser() on pages that have role restrictions applied to them we cannot use the session variable, because as stated the session variable has not been initialized yet when GetRolesForUser() is actually called (and I have found no way to force it into being at this early point in the request cycle).
3) The only option is to use a cookie, or use the Forms Authentication ticket's UserData field. I trawled through many theories about using session/cookie/IDs linked to an object stored in the application cache (which is available when the session is not) but ultimately the correct choice is to place this data in the authentication ticket.
4) If a user is logged on to a project it is via a ProjectName/UserName pair, hence anywhere we are tracking the user's authentication we require both these data. In trivial testing we can get away with the username in the ticket and the projectname in a separate cookie, however it is possible for these to get out of synch. For example if we use a session cookie for the projectname and tick "remember me" when we logon (creating a permanent cookie for the authentication ticket) then we can end up with a username but no projectname when the session cookie expires (browser is closed). Hence I manually add the project name to the UserData field of the authentication ticket.
5) I have not figured out how to manipulate the UserData field without explicitly setting a cookie, which means that my solution cannot work in "cookieless" session mode.
The final code turned out to be relatively simple.
I override the Authenticate event of the LoginView in the login page:
//
// Add project name as UserData to the authentication ticket.
// This is especially important regarding the "Remembe Me" cookie - when the authentication
// is remembered we need to know the project and user name, otherwise we end up trying to
// use the default project instead of the one the user actually logged on to.
//
// http://msdn.microsoft.com/en-us/library/kybcs83h.aspx
// http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.login.remembermeset(v=vs.100).aspx
// http://www.hanselman.com/blog/AccessingTheASPNETFormsAuthenticationTimeoutValue.aspx
// http://www.csharpaspnetarticles.com/2009/02/formsauthentication-ticket-roles-aspnet.html
// http://www.hanselman.com/blog/HowToGetCookielessFormsAuthenticationToWorkWithSelfissuedFormsAuthenticationTicketsAndCustomUserData.aspx
// http://stackoverflow.com/questions/262636/cant-set-formsauthenicationticket-userdata-in-cookieless-mode
//
protected void LoginUser_Authenticate(object sender, AuthenticateEventArgs e)
{
string userName = LoginUser.UserName;
string password = LoginUser.Password;
bool rememberMe = LoginUser.RememberMeSet;
if ( [ValidateUser(userName, password)] )
{
// Create the Forms Authentication Ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1,
userName,
DateTime.Now,
DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes),
rememberMe,
[ ProjectName ],
FormsAuthentication.FormsCookiePath);
// Create the encrypted cookie
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
if (rememberMe)
cookie.Expires = DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes);
// Add the cookie to user browser
Response.Cookies.Set(cookie);
// Redirect back to original URL
// Note: the parameters to GetRedirectUrl are ignored/irrelevant
Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, rememberMe));
}
}
I have this global method to return the project name:
/// <summary>
/// SQL Server database name of the currently selected project.
/// This name is merged into the connection string in EventConnectionString.
/// </summary>
public static string ProjectName
{
get
{
String _ProjectName = null;
// See if we have it already
if (HttpContext.Current.Items["ProjectName"] != null)
{
_ProjectName = (String)HttpContext.Current.Items["ProjectName"];
}
// Only have to do this once in each request
if (String.IsNullOrEmpty(_ProjectName))
{
// Do we have it in the authentication ticket?
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = identity.Ticket;
_ProjectName = ticket.UserData;
}
}
}
// Do we have it in the session (user not logged in yet)
if (String.IsNullOrEmpty(_ProjectName))
{
if (HttpContext.Current.Session != null)
{
_ProjectName = (string)HttpContext.Current.Session["ProjectName"];
}
}
// Default to the test project
if (String.IsNullOrEmpty(_ProjectName))
{
_ProjectName = "Test_Project";
}
// Place it in current items so we do not have to figure it out again
HttpContext.Current.Items["ProjectName"] = _ProjectName;
}
return _ProjectName;
}
set
{
HttpContext.Current.Items["ProjectName"] = value;
if (HttpContext.Current.Session != null)
{
HttpContext.Current.Session["ProjectName"] = value;
}
}
}
Can't you postback the project selection to some page, add that selection to the session, then redirect to appropriate protected page, where auth will kick in and force login?
ASP.NET session doesn't get created in the form of a cookie until you place at least one item in it.
How can I clear browser cache only on logout, sure I can use the below:
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
But this particular page which is a shopping bag page is accessible by both login and non-login users. How can I set it in such a way whereby the login user is able to access this page without clearing the browser cache but Only clears it when he/she logs out so that another user will not be able to access the history contents.
I have tried the solutions here:
http://www.codeproject.com/Tips/135121/Browser-back-button-issue-after-logout
made some changes but still couldn't figure out how to deal with this issue.
I also cleared my session on logout as below but I understand the browser cache will still stay.
FormsAuthentication.SignOut();
Session.Abandon();
Response.Redirect("~/");
Please advice. Thanks.
I am not a c# expert, but I am pretty sure what you have above only tells the browser to not cache the page you are on. There is no way to tell the browser to clear cache on any page. This would be a problem if there was such a way. Sounds like the solution you need is to not cache any page at all, regardless of logging out or not.
Perhaps you are getting muddled with the difference between server and client cache?
If you set output cache on your aspx page, that's server-side cache, and you have a scenario where .NET can decide whether to send pre-cached content or not, and still apply ACL rules.
If you set cache requirements on the HTTP you return using Response.Cache, that's client-side caching. Once the browser obeys the cache rules you send here, the only opportunity you will have to retract your cache rules is the next time the browser requests the page. If you set the cache to expire tomorrow, that's the next chance you'll get to amend the caching. Assuming the browser is obeying you, by the way, of which there is no guarantee.
In short, dynamic pages should not attempt to set client-side caching if you want them to stay dynamic. In fact you should actively use techniques such as the ones you mentioned to suppress Caching on those pages at all times.
Client-side caching should really only be used to assist with performance and bandwidth on the static parts of your site.
I am trying to solve a similar problem myself. This is just speculation, but if i could track a user specific header in my requests I was going to try using
HttpContext.Current.Response.Cache.VaryByHeaders["login"] = true;
in the global.asax
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if (arg == "login")
{
return User().Name;
}
return base.GetVaryByCustomString(context, arg);
}
There is a way to do it. If you are caching a page, you can add a vary parameter. For Example
[OutputCache(Duration = 60, Location = System.Web.UI.OutputCacheLocation.Client, VaryByParam = "random")]
[CompressFilter]
public ActionResult Page(PageModel model)
{
...
}
In the example above, if I pass a random variable like the ticks of the current datetime object, that will prevent the cache.