开发者

ASP.Net MVC 3 Strange Session Behaviour

开发者 https://www.devze.com 2023-03-10 03:11 出处:网络
I have an mvc 3 app for which I\'m implementing authorization using my own login view which checks if the users name and password are allowed and then sets a variable in the session to say that the us

I have an mvc 3 app for which I'm implementing authorization using my own login view which checks if the users name and password are allowed and then sets a variable in the session to say that the user is loggged in. This kind of works but for one particular view it is behaving in a strange undesirable way. The said view contains a form which I use to input some data and upload a file. For some reason which I can't figure out, after this form is posted a new session is started and therefore the variable which remembered that the user was logged in is reset to false and subsequently the login page is displayed again.

I'm lost as to why the application is starting a new session at this point? I have not instructed it to do this. Can anyone recommend solutions to stop this behaviour and get it to keep the old session?

Thanks.

UPDATE - Some Code:

Note the session seems to be terminated immediately after the response to the posted Create form

CMS controller which uses a custom Autorize attribute called "RDAutorize" on all actions:

[RDAuthorize]
public class PhotoCMSController : Controller
{

public ActionResult Create()
{
    /* Code omitted: set up a newPhoto object with default state */
    /* Display view containing form to upload photo and set title etc. */
    return View("../Views/PhotoCMS/Create", newPhoto);
}

[HttpPost]
public ContentResult Upload(int pPhotoId)
{   
    /* Code ommited: receive and store image file which was posted
     via an iframe on the Create view */  
    string thumbnail = "<img src='/path/to/thumb.jpg' />";
    return Content(thumbnail);
}

[HttpPost]
public ActionResult Create(string pPhotoTitle, string pCaption etc...)
{
     /*Code omitted: receive the rest of the photo data and save
      it along with a reference to the image file which was uploaded
      previously via the Upload action above.*/

      /* Display view showing list of all photo records created */
      return View("../Views/PhotoCMS/Index", qAllPhotos.ToList&l开发者_高级运维t;Photo>());

      /* **Note: after this view is returned the Session_End() method fires in 
       the Global.asax.cs file i.e. this seems to be where the session is
       being lost** */
}

}/*End of CMS Controller*/

Custom Authorize action filter:

public class RDAuthorize : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Boolean authorized = Convert.ToBoolean(
            HttpContext.Current.Session["UserIsAuthorized"]
        );

        if (!authorized) {
            /* Not logged in so send user to the login page */
            filterContext.HttpContext.Response.Redirect("/Login/Login");
        }
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext) {}
    public override void OnResultExecuting(ResultExecutingContext filterContext) {}
    public override void OnResultExecuted(ResultExecutedContext filterContext) {}

}/*End of Authorize Action Filter*/

Login controller:

public class LoginController : Controller
{
    private PhotoDBContext _db = new PhotoDBContext();

    public ActionResult Login()
    {
        string viewName = "";
        Boolean authorized = Convert.ToBoolean(Session["UserIsAuthorized"]);
        if (authorized)
        {
            viewName = "../Views/Index";
        }
        else
        {
            viewName = "../Views/Login/Login";
        }
        return View(viewName);
    }

    [HttpPost]
    public ActionResult Login(string pUsername, string pPassword)
    {
        string viewName = "";
        List<Photo> model = new List<Photo>();

        var qUsers = from u in _db.Users
                select u;

        foreach (User user in qUsers.ToList<User>())
        {
            /* If authorized goto CMS pages */
            if (pUsername == user.Username && pPassword == user.Password)
            {
                Session["UserIsAuthorized"] = true;
                var qPhotos = from p in _db.Photos
                              where p.IsNew == false
                              select p;

                model = qPhotos.ToList<Photo>();
                viewName = "../Views/PhotoCMS/Index";
                break;
            }
        }

        return View(viewName, model);

    }

}/* End of Login controller */


Turns out the whole ASP.Net application was restarting because as part of the photo upload I was storing the image file in a temporary folder and then deleting the directory after moving the file to a permanent location. Apparently its default behaviour for ASP.Net to restart if a directory within the web site is deleted. I found this post which describes the problem and offers a solution whereby the following code is added to the Global.asax.cs file. Implementing this solution has fixed the problem. The fix is applied by calling FixAppDomainRestartWhenTouchingFiles() from the Application_Start() event:

    protected void Application_Start()
    {
        FixAppDomainRestartWhenTouchingFiles();
    }

    private void FixAppDomainRestartWhenTouchingFiles()
    {
        if (GetCurrentTrustLevel() == AspNetHostingPermissionLevel.Unrestricted)
        {
            /* 
             From: http://www.aaronblake.co.uk/blog/2009/09/28/bug-fix-application-restarts-on-directory-delete-in-asp-net/
             FIX disable AppDomain restart when deleting subdirectory
             This code will turn off monitoring from the root website directory.
             Monitoring of Bin, App_Themes and other folders will still be 
             operational, so updated DLLs will still auto deploy.
            */

            PropertyInfo p = typeof(HttpRuntime).GetProperty(
                "FileChangesMonitor", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
            object o = p.GetValue(null, null);
            FieldInfo f = o.GetType().GetField(
                "_dirMonSubdirs", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase);
            object monitor = f.GetValue(o);
            MethodInfo m = monitor.GetType().GetMethod(
                "StopMonitoring", BindingFlags.Instance | BindingFlags.NonPublic);
            m.Invoke(monitor, new object[] { });
        }
    }

    private AspNetHostingPermissionLevel GetCurrentTrustLevel()
    {
        foreach (AspNetHostingPermissionLevel trustLevel in
            new AspNetHostingPermissionLevel[] {
                AspNetHostingPermissionLevel.Unrestricted,
                AspNetHostingPermissionLevel.High,
                AspNetHostingPermissionLevel.Medium,
                AspNetHostingPermissionLevel.Low,
                AspNetHostingPermissionLevel.Minimal }
            )
        {
            try
            {
                new AspNetHostingPermission(trustLevel).Demand();
            }
            catch (System.Security.SecurityException)
            {
                continue;
            }

            return trustLevel;
        }

        return AspNetHostingPermissionLevel.None;
    }


Since sessions are associated with cookies, they are available for a specific domain.

It's a common mistake to ask for a session variable in the same application while the domain has changed (i.e. redirecting to a subdomain).


Does the controller action that you are posting the form contains any [Authorize] attribute. You need to post some code.


  1. Verify a new session is really started every time. Check Trace output for the user's session id to ensure it realllly has changed.
  2. Ensure the cookie getting sent over is actually getting set and sent over. (called ASPsessionIDSOMETHING ) and if that is being sent by the browser. Download the tool Fiddler to check the cookies easily (set cookie header coming from the server and the request cookies going back to the server from the browser. Make sure your browser is accepting the cookie and you dont say... have cookies turned off.
  3. If your session id is changing at every request then your session isn't properly getting set the first time, set a break point on that code if you havent already.
  4. You can log when the worker process resets - ensure that isn't the case. see http://www.microsoft.com/technet/prodtechnol/windowsserver2003/library/IIS/87892589-4eda-4003-b4ac-3879eac4bf48.mspx


I had the same problem. The problem only occured when a post request was send to the server and the session was not modified during that request. What I did as a workaround was, to write a custom filter which does nothing more than writing a key / value into the session on each request and added that filter to the GlobalFilter collection in the global.asax.

public class KeepSessionAlive : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if(filterContext.HttpContext.Session != null)
        {
            filterContext.HttpContext.Session["HeartBeat"] = DateTime.Now.ToShortDateString();
        }
}

    public void OnActionExecuted(ActionExecutedContext filterContext) { }

}

And in the global.asax:

protected override void AddCustomGlobalFilters(GlobalFilterCollection filters)
{
   filters.Add(new KeepSessionAlive());
}

This might be not the best solution but it helped me in my case.

0

精彩评论

暂无评论...
验证码 换一张
取 消