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.
Related
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?) :)
Please bear with me as I am pretty new to designing websites.
When someone goes to my website they are initially sent to a log in page, which is defined in my web.config as:
<authentication mode="Forms">
<forms loginUrl="~/Login/Index" timeout="15"/>
</authentication>
However, before they log in I check whether the database they want to access has been defined (this is something that users may want to change frequently), and if it has not I want to send them to a different form. So, my login controller index looks like:
public ActionResult Index()
{
bool settingsSetUp = SupportLibrary.Settings.CompanyId != null;
if (settingsSetUp)
return View();
else
return RedirectToAction("index", "setup");
}
However, when I try this I always get "This page has a redirect loop" in Chrome. The page will not display in Firefox of IE either. On investigation the above method is always being called numerous times, so that eventually the browser decides it is being redirected too often. If I just set it to go to the view associated with the controller (no redirection) it calls the above method 15 times. Otherwise it is called 10 times before Chrome displays the error message.
Does anyone know why it is being called so many times as I think that is the root of the problem? Many thanks!
You are trying to load actions that require the user to be authenticated (they either have the Authorize attribute on them or it has been applied globally) which is causing a redirect back to the login action.
Check your actions to ensure that they can be accessed without being logged in if required.
I have an web service call where I get a list of 5 access codes, a user will be able to login with one of those codes(basically feel special that they have the code, but nothing secure about it, as they could share codes if they wanted to)
I would want to be able to use the [Authorize] if at all possible. Won't be using a database, just that one api call. Is this possible?
Check the codes on login with a simple if statement
if{code1 == "edgwreggw" || code2 == "etgwg"....)
{
FormsAuthentication.RedirectFromLoginPage(data.username, true);
}
else
{
login fail
}
Webconfig
<authentication mode="Forms">
<forms loginUrl="~/UserAuthentication/SignIn" timeout="10" defaultUrl="~\Home\Index" />
</authentication>
So.. you really don't care about security but you want to hand out 1 of 5 codes to random people to use your web service.
Simple enough. Put the codes in an array. Check if the code passed in is one of those values. If not, end the request. If it is, process the request.
First post, I'm a complete .Net/C# novice thrown in at the deep end!
I've inherited a C# wed application due to someone leaving at work and me being the only one with bandwidth! But not the .Net, C# knowledge!
The app is used by people on different sites all over the world. They log in using the corporate login details and as such they log into different servers depending on where they are located, (Europe, America or India).
The guy who wrote the app couldn't work out how to switch the ConnectionString in web.config depending on location, so duplicated the whole app for each domain! With the only variation being a single IP address in web.config for each duplicated version of the app! Then did a simple web front page which took the user to "their" version of the app depending on where they said they were in the world!
The first thing I want to do is to move to a single version to maintain, so I need to be able to switch the connection string or how to login?
I've spent several days trying to work out how I get to ConnectionString (defined in web.config) from my Login class, only to discover the values set in web.config seem to be read only, so I can't alter them.
So I guess the first question is, am I barking up the wrong tree? Can I just set all the information that AspNetActiveDirectoryMembershipProvider (see code later) requires and call it from my login class? Or is the ConnectionString route the Ipso facto way to set up connections in .Net/C#? So therefor I do need to find out how to change/specify/add the value at runtime.
Three possibilities I can think of:- (The first is the one I've ground to a hult with)
Change the ConnectionString for ADService in my web.config from my Login class?
Change what AspNetActiveDirectoryMembershipProvider uses, so from my Login class magicly get it to use EMEA_ADService or PACIFIC_ADService as defined in web.config?
Is it possible to define a new connectionString and call AspNetActiveDirectoryMembershipProvider all from my Login class, not using web.config for this connection at all?
Here's a bit of my/his web.config file and my Login class
Cuttings from Web.config
<connectionStrings>
<add name="ADService" connectionString="LDAP://12.345.67.8" /> *---- Original ConnectionString (IP address changed)----*
<add name="EMEA_ADService" connectionString="LDAP://12.345.67.8" /> *---- Added by me playing around unsuccessfully! ----*
<add name="PACIFIC_ADService" connectionString="LDAP://12.345.67.9" /> *---- Added by me playing around unsuccessfully! ----*
~
</connectionStrings>
<authentication mode="Forms">
<forms loginUrl="~/Login.aspx" timeout="2880" /> *---- The background class for this popup (Login.aspx.cs) is where I'm currently trying to affect ConnectionString----*
</authentication>
*---- Pretty sure this is the bit that actually does the login verification----*
<membership defaultProvider="AspNetActiveDirectoryMembershipProvider">
<providers>
<clear />
<add name="AspNetActiveDirectoryMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=12345678" connectionStringName="ADService" applicationName="/." description="ADService" />
</providers>
</membership>
This is as far as I've got in my class before finding out that I don't appear to be able to alter ConnectionString!
Cuttings from Login.aspx.cs
public partial class Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ConnectionStringSettingsCollection connections = ConfigurationManager.ConnectionStrings; //this is now working :)
string userDomain = Environment.UserDomainName; //Probably don't need this, it seems to give the login domain on the machine. Don't know yet if that will be the users machine or the server the app runs on?
if (connections.Count != 0)
{
foreach (ConnectionStringSettings connection in connections)
{
string testname = connections["ADService"].Name;
string testConnectionString = connections["ADService"].ConnectionString;
connections["ADService"].ConnectionString = "LDAP://12.345.67.9";
testConnectionString = connections["ADService"].ConnectionString;
Any hint would be very welcome!
P.S. I have requested a .Net/C# course at work! ;)
You wouldn't want to alter the existing connection string. Rather, you'd want to alter which connection string your Data Access Layer was using to call different service stacks. You could then choose a connection string at runtime based on whatever input parameters you wanted to use. which in your case might be an IP range.
asp.net mvc multiple connection strings
Handling multiple connection strings in ONE DataAccess Layer
http://msdn.microsoft.com/en-us/library/aa479086.aspx
The microsoft article is particularly interesting since it actually takes an architectural look at proper patterns for resolving dilemmas like yours. I think you got stuck with the short end of the stick! Best of luck!
The Web.config cannot be modified at Runtime. I would suggest setting some kind of flag via a login link or combobox on the website for people to use to choose where they want to login. It is not the job of the server to figure out what a user wants to do.
Is it possible to vary the output cache in MVC based on certain values in the session? I've read a lot about using the varybycustom functionality and overriding GetVaryByCustomString in Global.asax but the session is not available at this point.
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "somekey")
//Want to check the session here (but it isn't available).
return base.GetVaryByCustomString(context, custom);
}
I understand this is because the Session isn't created until later in the request pipeline.
My concern is that without varying the cache based on the user's session, the page (which changes based on what the user has in the session, has additional HTML specific to that user etc) will get cached (as the URL is the same) and served by our load balancer, proxy servers etc. and then served to other requests with other people's session information on the page!
The reason the URL is the same is that the user comes in as a 'guest', enters some information (POST), this is validated and stored in the session and then they are re-directed back to the same page (which should now be specific to the user based on the session data).
The page itself should be cached normally because if a 'guest' visits the same URL, it should serve the same 'standard' page every time.
Is is possible to vary the caching in this way?
If you want to personalize the cache output per user, it is better you set the Location to OutputCacheLocation.Client as below. More information here
[OutputCache(Duration=3600, VaryByParam="none", Location=OutputCacheLocation.Client, NoStore=true)]
public string GetName()
{
return "Hi " + User.Identity.Name;
}
Would a Output Cache ActionFilter help at all?
Or perhaps you could refactor your view in to a layout page plus partial views for anonymous and authenticated sections, then utilize Partial Caching.
You should look into "Donut Caching", but this isn`t supported by ASP.NET MVC 3, at least not out of the box. Fortunately somebody already solved this problem for you see MvcDonutCaching
I read that ASP.NET MVC 4 will include "Donut Hole Caching" out of the box, but i cant tell if it's in the current RC or not.