ASP.NET session stateid issue - asp.net

i have a load balancing application, and we have an issue where a user try to log in, he/she will then see some else's name.
during the logout process we only call "Session.Abandon();". The article in http://support.microsoft.com/kb/899918 states the following:
When you abandon a session, the session ID cookie is not removed from
the browser of the user. Therefore, as soon as the session has been
abandoned, any new requests to the same application will use the same
session ID but will have a new session state instance. At the same
time, if the user opens another application within the same DNS
domain, the user will not lose their session state after the Abandon
method is called from one application.
does anyone have a clue of why the user sees some else's name while loging in?
Edited - code:
public class CacheManager
{
public Contact.Contact User
{
get { return HttpContext.Current.Session["Contact"] as Contact.Contact; }
set { HttpContext.Current.Session["Contact"] = value; }
}
}
header.ascx.cs
/// <summary>
/// This is the session manager
/// </summary>
public readonly CacheManager Localcache = new CacheManager();
public Login()
{
var contact = BAL.Contact.Get(this.UserName, this.Password);
Localcache.User = contact;
Response.Redirect(Request.Url.AbsoluteUri);
}
protected void Logout_Click(object sender, ImageClickEventArgs e)
{
Session.Abandon();
Response.Redirect(Constants.HomePage);
}
header.ascx:
You are logged in as
<br />
<span class="user_desc">
<%= string.Format("{0} {1}",LocalCache.User.FirstName ,LocalCache.User.LastName)%></span>

when a user explicitly logs out and a new user accesses the page (assuming the browser wasn't closed), the browser still maintains the sessionId (even with Session.Clear() and Session.Abandon()).
Try to add these lines of code
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
Link to check

Related

Only allow 1 user login in Spring Sercurity

I have two users: A and B, if user A login first, user B cant login util user A logout. Each user require three login info: storeId, storePassword, userPassword.
If user B same storeId with user A, do not allow login
If user B different storeId with user A, allow login
I use ServletContext to hold users logged, and when logged user click logout, I will remove that user from ServletContext. But I cant hanle when user close brower intead of click logout. I think this is not a good idea
Here is my code
#Override
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
//redirectStrategy.sendRedirect(request, response, "/login");
// do whatever you want
ServletContext context = request.getSession().getServletContext();
Object _auths = context.getAttribute("_authentications");
if(_auths != null) {
List<String> auths = (List<String>) _auths;
auths.remove(authentication.getName());
if(auths.size() == 0) {
auths = new ArrayList<String>();
}
context.setAttribute("_authentications", auths);
}
super.onLogoutSuccess(request, response, authentication);
}
does anyone give me a good solution?
Thank you
i have solved my problem, i use SessionRegistry in AuthencationProvide intead of ServletContext
#Autowired
#Qualifier("sessionRegistry")
private SessionRegistry sessionRegistry;
List<Object> principals = sessionRegistry.getAllPrincipals();
for (Object principal: principals) {
String[] auths = principal.toString().split(StringPool.DASH);
if(auths.length == 4 && auths[1].equals(storeId)) {
throw new BadCredentialsException(auths[0]+StringPool.DASH+auths[1]);
}
}
this code work well when session time out, user close brower. And i dont need any js source code to handle
You can use the js below to remove user from servletContext on window close:
<script type="text/javascript">
window.onbeforeunload = logout;
function logout(){
// Make an ajax call to remove user from ServletContext
alert("Window close is being called");
return false;
}
</script>

how to prevent the user repeat-login

In my application,I do not want two user login with the same login name.
For example, user1 login with name "test1",then user2 try to login with "test1" too,but at this moment the user1's formauthentication does not expire,so the login of user2 should be denied.
I created a cache class to record all the active session:
public class SessionDb {
private static Dictionary<string, HttpSessionState> sessionDB=new Dictionary<string, HttpSessionState>();
public SessionDb() {
}
public static void addUserAndSession(string name, HttpSessionState session) {
sessionDB.Add(name, session);
}
public static bool containUser(string name) {
//in fact,here I also want to check if this session is active or not ,but I do not find the method like session.isActive() or session.HasExpire() or something else.
return sessionDB.ContainsKey(name);
}
public static void removeUser(string name) {
if(sessionDB.ContainsKey(name)) {
sessionDB.Remove(name);
}
}
}
In the login.aspx.cs:
//check the name and password
if(checkNameAndPass(sUserName, sUserPwd)) {
if(!SessionDb.containUser(sUserName)) {
//let the user login
Session["current_user_name"] = sUserName;
SessionDb.addUserAndSession(sUserName, Session);
FormsAuthentication.RedirectFromLoginPage(UserName.Text, false);
}
else {
//
this.error.Text=string.Format("user {0} have logined!", sUserName);
}
}
Global.asax:
void Session_End(object sender, EventArgs e)
{
SessionDb.removeUser(Session["current_user_name"].ToString());
}
But it seems that it the Session_End() method is called at some time according the timeout setting in the sessionState.
Obviously I need the the SessionDb remove the related session when the authentication timeout.
Any idea to improve my code? or any other idea to implement my requirement?
I just do not want the user repeat-login(with the same name).
UPDATE:
BTW,I think my code also have some problems: I store the log in token using the Session,but how about if the formauthentication have timeout but the session does not?
If you are using a Membership provider:
A user is considered online if the current date and time minus the UserIsOnlineTimeWindow property value is earlier than the LastActivityDate for the user.
from MSDN Article
so you can simple use the check
Membership.IsOnline();
before you login the user.
In asp.net site how to prevent multiple logins of same user id?
Another approach (Disclaimer: I have not tested this.):
In the web.config add userIsOnlineTimeWindow to 1 and in the loggingIn event handler:
protected void Login1_LoggingIn(object sender, LoginCancelEventArgs e)
{
MembershipUser u = Membership.GetUser(Login1.UserName);
Response.Write(u.IsOnline);
if (u.IsOnline)
{
Login1.FailureText = "A user with this username is already logged in.";
e.Cancel = true;
}

Custom authentication module inheriting IHttpModule issue

LoginPage.aspx:-
protected void Button1_Click(object sender, EventArgs e)
{
Context.Items["Username"] = txtUserId.Text;
Context.Items["Password"] = txtPassword.Text;
//
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, Context.Items["Username"].ToString(), DateTime.Now, DateTime.Now.AddMinutes(10), true, "users", FormsAuthentication.FormsCookiePath);
// Encrypt the cookie using the machine key for secure transport
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName, // Name of auth cookie
hash); // Hashed ticket
// Set the cookie's expiration time to the tickets expiration time
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
Response.Cookies.Add(cookie);
Response.Redirect("Default.aspx");
}
Global.asax file:-
void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id =
(FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
// Get the stored user-data, in this case, our roles
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(id, roles);
Response.Write(HttpContext.Current.User.Identity.Name);
Response.Redirect("Default.aspx");
}
}
}
}
I get the following error after signing in
This webpage has a redirect loop.
The webpage at http://localhost:1067/Default.aspx has resulted in too many redirects. Clearing your cookies for this site or allowing third-party cookies may fix the problem. If not, it is possibly a server configuration issue and not a problem with your computer.
This is the rough idea of what your module should look like. Your module will run on every request. You don't invoke it or pass anything to it, it just automatically fires whenever a request is made that ASP.Net is set to process.
Your module will do two things, 1) authenticate a user in the login page, 2) authenticate a user on subsequent pages. The first step is to subscribe to the BeginRequest method which will be given the current HttpApplication as the first parameter. From there you need to determine if the user is on your login page or not. If they're not on your login page, check your session or cookie or querystring token, or whatever you're using to make sure that they're still valid. If they're invalid, bounce them back to the login page.
If they're on your login page and have made a POST, look at the raw form fields and validate them. TextBoxes, checkboxes, etc don't exist here, only raw form fields. If they're valid, set your authentication token however you want (session, cookies, etc). If they're invalid, either redirect to the login page or inject a "try again" message or something.
Also, if you double-post a message please reference it so that we can follow the chain of what was already said.
class MyModule : IHttpModule
{
void IHttpModule.Init(HttpApplication context)
{
//Subscribe to the BeginRequest event
context.BeginRequest += new EventHandler(this.Application_BeginRequest);
}
private void Application_BeginRequest(Object source, EventArgs e)
{
//Initialize our variables, null checks should be put here, too
HttpApplication app = (HttpApplication)source;
HttpContext context = app.Context;
System.Web.SessionState.HttpSessionState s = context.Session;
//Normally our module needs to validate every request to make sure our request is still authenticated.
//The exception to that rule is on our logon page where they obviously don't have credentials yet.
if(!context.Request.FilePath.ToLowerInvariant().StartsWith("/login.aspx")){
//If we're here then we're not on the logon page, validate our current session according to whatever logic we want
if (s != null && s["isvalid"] == "true"){
return;
}else{
context.Response.Redirect("/login.aspx");
}
}else{
//If we're here then we're on the login page itself. If there's a post, assume that they've hit the login button
if (context.Request.HttpMethod == "POST")
{
//Whatever your form variables are called
string username = context.Request.Form["username"];
string password = context.Request.Form["password"];
//Your own validation logic would go here
if (MyCustomLogin.IsUserValid(username, password))
{
s["isvalid"] = "true";
context.Response.Redirect("/Home.aspx");
}else{
s["isvalid"] = "false";
context.Response.Redirect("/login.aspx?error=invalid_login");
}
}else{
//If we're here then the request is probably a GET or HEAD which would be from a person
//initially browsing to our page so just do nothing and pass it through normally
}
}
}
}
There is no direct way to have access to this information in the module (for authenticated user, you can access the username via the context, but not the password). The module checks if a request is carrying required authentication information and serve or deny the request based on that. Unless you deliberately from the login page collect this information and store somewhere where you can access it in the module, e.g session. But ideally, storing password is not widely recommended, collect it use it for authentication and destroy.
You might ideally throw more light on the reason why you want to have access to this information in the module and guys can then suggest methods to accomplish it.
Edited, after Chandan comment:
#Chandan, your comment here suggest to me what you want to do is use httpmodule for your authentication as against using standard form authentication. If I am on track, then you can check this project on codeproject at http://www.codeproject.com/KB/web-security/AspNetCustomAuth.aspx. Goodluck

How to handle "Remember me" in the Asp.Net Membership Provider

Ive written a custom membership provider for my ASP.Net website.
Im using the default Forms.Authentication redirect where you simply pass true to the method to tell it to "Remember me" for the current user.
I presume that this function simply writes a cookie to the local machine containing some login credential of the user.
What does ASP.Net put in this cookie? Is it possible if the format of my usernames was known (e.g. sequential numbering) someone could easily copy this cookie and by putting it on their own machine be able to access the site as another user?
Additionally I need to be able to inercept the authentication of the user who has the cookie. Since the last time they logged in their account may have been cancelled, they may need to change their password etc so I need the option to intercept the authentication and if everything is still ok allow them to continue or to redirect them to the proper login page.
I would be greatful for guidance on both of these two points. I gather for the second I can possibly put something in global.asax to intercept the authentication?
Thanks in advance.
For me the solution was differentiating between a browser-session auth cookie (not to be confused with the asp.net session cookie) and a persistent one - setting a low expiration will create a persistent cookie meaning it gets remembered when the browser is closed and re-opened within the expiration time. The following works for me:
public void SetAuthenticationCookie(LoginView loginModel)
{
if (!loginModel.RememberMe)
{
FormsAuthentication.SetAuthCookie(loginModel.Email, false);
return;
}
const int timeout = 2880; // Timeout is in minutes, 525600 = 365 days; 1 day = 1440.
var ticket = new FormsAuthenticationTicket(loginModel.Email, loginModel.RememberMe, timeout);
//ticket.
string encrypted = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encrypted)
{
Expires = System.DateTime.Now.AddMinutes(timeout),
HttpOnly = true
};
HttpContext.Current.Response.Cookies.Add(cookie);
}
FormsAuthentication and MembershipProviders are two completely different things, still they are made to work with each other very well. If you have written a persistent cookie ["Remember Me"] then next time, you can simply call Membership.GetUser() which will return you the MembershipUser instance of the currently logged in user or null if no user is logged in.
So first time when user arrives and authenticates with "Remember Me", you shall write a persistent cookie as following.
FormsAuthentication.RedirectFromLoginPage(strUserName, true);
Assuming user does not logout and leaves webpage and comes back after sometime. You can simply call MembershipUser.GetUser() as following and check if the user is already logged from the persistent cookie written by FormsAuthentication.
MembershipUser someUser = Membership.GetUser();
if(someUser == null)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
else
{
//Take where logged in users go.
}
You can do this check on your Login page itself or main landing page to intercept the User account to check if he needs to change the password or if the account is disabled as in your case.
EDIT
There are two ways to do this.
1.) Check for authentication as mentioned above in Session_Start event in global.asax and set a session key that becomes available on all pages for that particular session.
2.) Another way is too keep a common application wide common PageBase class that inherits from System.Web.UI.Page and acts as base page class for all your asp.net pages. On the Page Load of the common PageBase class check for the authentication as mentioned above. You will have to carefully write conditional redirection in this case since this might head towards infinite redirection with no end since it will run on Page_Load of all page from the common PageBase class.
public class PageBase : System.Web.UI.Page
{
/// <summary>
/// Initializes a new instance of the Page class.
/// </summary>
public Page()
{
this.Load += new EventHandler(this.Page_Load);
}
private void Page_Load(object sender, EventArgs e)
{
try
{
AuthenticateUser();
}
catch
{
//handle the situation gracefully.
}
}
private AuthenticateUser()
{
MembershipUser someUser = Membership.GetUser();
if(someUser == null)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
else
{
//Take where logged in users go.
}
}
}
//in your asp.net page code-behind
public partial class contact : PageBase
{
protected void Page_Load(object sender, EventArgs e)
{
}
}

How to set Thread.CurrentPrincipal for use throughout the application?

In an ASP.net application I'm using a Login control with a custom membership provider that I wrote. What I want to do is to set Thread.CurrentPrincipal to my custom Principal object, just after the user is authenticated.
I'm using the setter: Thread.CurrentPrincipal and it sets the Principal object for me but, on all the consequent threads this CurrentPrincipal is overridden with the default one.
Here is my code for the Authenticate event of the Login control:
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
string username = Login1.UserName;
string password = Login1.Password;
if (Membership.ValidateUser(username, password))
{
var login = sender as Login;
var phoenixIdentity = new PhoenixIdentity("B", "Forms" , true);
var principal = new PhoenixPrincipal(phoenixIdentity);
Thread.CurrentPrincipal = principal;
AppDomain.CurrentDomain.SetThreadPrincipal(principal);
HttpContext.Current.User = principal;
e.Authenticated = true;
}
}
For example, imagine that I login with the username A, everything goes well... Validation passes, but I hardcode the user with the username B in the Identity object which is set to the Principal object I set as the CurrentPrincipal object.
When I check which user is set to the CurrentPrincipal Identity at the end of this method it says it's user B. But when I load another page and then check what the Identity of the CurrentPrincipal is, it says it's user A.
So, how can I make my CurrentPrincipal object to be persistent across all other threads, and where/when does this Login control set the CurrentPrincipal object of the Thread?
Tadas is not wrong, FormsAuthentication correctly implemented will not cause this problem.
Your page is accessible even without login, only in the login page, your thread's principle is set manually by you, but when you hit the other URL, it sure doesnt call your login page and remember each page runs on its own different thread. If you request first page and set thread principle and you request second page in same browser instance, it may or may not be the exact same thread.
This is how FormsAuthentication works,
It checks if Auth Cookie is set or not, it then directs user to login page
Login page must validate and set auth cookie, like FormsAuthentication.SetAuthCookie
Before every page access, Step 1 is executed.
After successful validation of Auth Cookie, ASP.NET internally sets the current user and all differnet parameters according to your membership component.
ASP.NET Global.asax file can give you some events where in you can plugin your code to check just after authentication is successful you can change your current user, remember setting your current principle on login page will not help
We had similar issue when we were using session to store certain important information, after auth sessions were not rebuilt, so we wrote a HTTP Module, and in it's init method, we attached AfterRequestAcquired event and in this event you can write your code to instantiate all your important user related variables.
You can handle FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs e) (in Global.asax) and set CurrentPrincipal here.
void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs e)
{
var phoenixIdentity = new PhoenixIdentity("B", "Forms" , true);
var principal = new PhoenixPrincipal(phoenixIdentity);
e.User = principal;
}
This is what I did in FormsAuthentication_OnAuthenticate method:
if (FormsAuthentication.CookiesSupported)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value);
var myIdentity = new GenericIdentity("B");
var principal = new GenericPrincipal(myIdentity, new string[]{"rola1"});
e.User = principal;
}
catch (Exception ex)
{
// Decrypt method failed.
}
}
}
else
{
throw new HttpException("Cookieless Forms Authentication is not " +
"supported for this application.");
}
it seems that it's working what it should do... It's just that if I put my custom principal/identity pair as e.User, then I have serialization problem which I need to fix next... Thank you guys...

Resources