会话过期时重定向无法正常工作

Redirection does not work properly when the session expires

我在 Asp.Net MVC 5 和 C# 中有一个项目,并使用自定义 session state customizing SessionStateStoreProviderBase class in Oracle, like this example。我的问题是,当会话过期时,屏幕会变成空白页,只有当用户按下 F5 时,才会第二次加载登录屏幕。只有按两次 f5 后,它才能工作。

有没有人遇到过这个错误?

更新

这是使用的class

    public class ProvedorSessao : SessionStateStoreProviderBase
{
    private SessionStateSection _pConfig;
    private string _connectionString;
    private ConnectionStringSettings _pConnectionStringSettings;
    private const string EventSource = "AgpSessionDataProvider";
    private const string EventLog = "Application";
    private const string ExceptionMessage = "SessionCustom - Ocorreu um erro interno na sessão.";
    private const int Timeout = 1; //One minute in test app default is 60
    private bool _pWriteExceptionsToEventLog;
    private bool WriteExceptionsToEventLog => _pWriteExceptionsToEventLog;
    private string ApplicationName { get; set; }

    public override void Initialize(string name, NameValueCollection config)
    {
        try
        {
            if (name.Length == 0)
                name = "PortalCustomSessionProvider";

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Session State Store provider");
            }

            base.Initialize(name, config);

            ApplicationName = System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;

            Configuration cfg = WebConfigurationManager.OpenWebConfiguration(ApplicationName);
            _pConfig = (SessionStateSection)cfg.GetSection("system.web/sessionState");

            _pConnectionStringSettings = ConfigurationManager.ConnectionStrings[config["connectionStringName"]];

            if (_pConnectionStringSettings == null || _pConnectionStringSettings.ConnectionString.Trim() == "")
            {
                throw new ProviderException("A string de conexão não pode ser vazia.");
            }

            _connectionString = "MyStringConection"; //My String Conection oracle

            _pWriteExceptionsToEventLog = true;

            if (config["writeExceptionsToEventLog"] != null)
            {
                if (config["writeExceptionsToEventLog"].ToUpper() == "TRUE")
                    _pWriteExceptionsToEventLog = true;
            }
        }
        catch (Exception e)
        {
            if (WriteExceptionsToEventLog)
            {
                WriteToEventLog(e, "Initialize");
                throw new ProviderException(ExceptionMessage);
            }

            throw new Exception(e.Message);
        }
    }

    public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
    {
        //Serializar o SessionStateItemCollection como uma string.
        var sessItems = Serialize((SessionStateItemCollection)item.Items);
        var conn = new OracleConnection(_connectionString);
        OracleCommand cmd;
        OracleCommand deleteCmd = null;

        if (newItem)
        {
            deleteCmd = new OracleCommand("DELETE FROM Sessions WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName AND Expires < :Expires", conn);
            deleteCmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
            deleteCmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
            deleteCmd.Parameters.Add("Expires", OracleDbType.Date).Value = DateTime.Now;

            // Insere um novo item na sessão.
            cmd = new OracleCommand("INSERT INTO Sessions " +
              "(SessionId, ApplicationName, Created, Expires, " +
              "LockDate, LockId, Timeout, Locked, SessionItems, Flags, UsuarioId, Menuid) " +
              "Values(:SessionId, :ApplicationName, :Created, :Expires, :LockDate, :LockId, :Timeout, :Locked, :SessionItems, :Flags, :UsuarioId, :Menuid)", conn);
            cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
            cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
            cmd.Parameters.Add("Created", OracleDbType.Date).Value = DateTime.Now;
            cmd.Parameters.Add("Expires", OracleDbType.Date).Value = DateTime.Now.AddMinutes(item.Timeout);
            cmd.Parameters.Add("LockDate", OracleDbType.Date).Value = DateTime.Now;
            cmd.Parameters.Add("LockId", OracleDbType.Int32).Value = 0;
            cmd.Parameters.Add("Timeout", OracleDbType.Int32).Value = item.Timeout;
            cmd.Parameters.Add("Locked", OracleDbType.Int32).Value = Convert.ToInt32(false);
            cmd.Parameters.Add("SessionItems", OracleDbType.Varchar2, sessItems.Length).Value = sessItems;
            cmd.Parameters.Add("Flags", OracleDbType.Int32).Value = 0;
            cmd.Parameters.Add("UsuarioId", OracleDbType.Varchar2, 50).Value = "";
            cmd.Parameters.Add("Menuid", OracleDbType.Varchar2, 50).Value = "";
        }
        else
        {
            var usuarioid = "";
            var menuid = "";
            if (item.Items != null)
            {
                usuarioid = string.IsNullOrEmpty(item.Items["codUsuario"] + "")
                    ? ""
                    : item.Items["codUsuario"].ToString();
                menuid = string.IsNullOrEmpty(item.Items["codMenu"] + "")
                    ? ""
                    : item.Items["codMenu"].ToString();
            }

            cmd = new OracleCommand("UPDATE Sessions SET Expires = :Expires, SessionItems = :SessionItems, Locked = :Locked, usuarioid = :usuarioid, menuid = :menuid " +
              " WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName AND LockId = :LockId", conn);
            cmd.Parameters.Add("Expires", OracleDbType.Date).Value = DateTime.Now.AddMinutes(item.Timeout);
            cmd.Parameters.Add("SessionItems", OracleDbType.Varchar2, sessItems.Length).Value = sessItems;
            cmd.Parameters.Add("Locked", OracleDbType.Int32).Value = Convert.ToInt32(false);
            cmd.Parameters.Add("usuarioid", OracleDbType.Varchar2, 50).Value = usuarioid;
            cmd.Parameters.Add("menuid", OracleDbType.Varchar2, 50).Value = menuid;
            cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
            cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
            cmd.Parameters.Add("LockId", OracleDbType.Int32).Value = lockId;
        }

        try
        {
            conn.Open();

            deleteCmd?.ExecuteNonQuery();

            cmd.ExecuteNonQuery();
        }
        catch (Exception e)
        {
            if (WriteExceptionsToEventLog)
            {
                WriteToEventLog(e, "SetAndReleaseItemExclusive");
                throw new ProviderException(ExceptionMessage);
            }
            else
            {
                throw new Exception(e.Message);
            }
        }
        finally
        {
            conn.Close();
        }
    }

    public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
    {
        return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actionFlags);
    }

    public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
    {
        return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actionFlags);
    }

    private SessionStateStoreData GetSessionStoreItem(bool lockRecord, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
    {
        //Valores iniciais de retorno e parâmetros de saída.
        SessionStateStoreData item = null;
        lockAge = TimeSpan.Zero;
        lockId = null;
        locked = false;
        actionFlags = 0;

        var conn = new OracleConnection(_connectionString); //Conexão com o banco
        OracleDataReader reader = null;
        var serializedItems = ""; //Variável para armazenar serialização SessionStateItemCollection.
        var foundRecord = false; //True se um registro for encontrado na base de dados.
        var deleteData = false; //True se o item de sessão retornou expirou e precisa ser eliminado.
        var timeout = 0; //Valor do tempo limite de armazenamento de dados.

        try
        {
            conn.Open();

            // LockRecord se for True é chamado de GetItemExclusive e 
            // falso se for chamado de GetItem.
            // Obtem um bloqueio, se possível. Ignorar o Registro se elemento expirou.
            OracleCommand cmd;
            if (lockRecord)
            {
                cmd = new OracleCommand(
                  "UPDATE Sessions SET" +
                  " Locked = :Locked1, LockDate = :LockDate " +
                  " WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName AND Locked = :Locked2 AND Expires > :Expires", conn);
                cmd.Parameters.Add("Locked1", OracleDbType.Int32).Value = Convert.ToInt32(true);
                cmd.Parameters.Add("LockDate", OracleDbType.Date).Value = DateTime.Now;
                cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
                cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
                cmd.Parameters.Add("Locked2", OracleDbType.Int32).Value = Convert.ToInt32(false);
                cmd.Parameters.Add("Expires", OracleDbType.Date).Value = DateTime.Now;

                locked = cmd.ExecuteNonQuery() == 0;
            }

            cmd = new OracleCommand(
              "SELECT Expires, SessionItems, LockId, LockDate, Flags, Timeout, Locked, usuarioid, menuid " +
              "FROM Sessions " +
              "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName", conn);
            cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
            cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;

            reader = cmd.ExecuteReader(CommandBehavior.SingleRow);
            while (reader.Read())
            {
                DateTime expires = reader.GetDateTime(0);

                if (expires < DateTime.Now)
                {
                    locked = false; //O registro foi expirado. Marca como não locked.
                    deleteData = true; //O registro foi deletado. Marca como deletado.
                }
                else
                    foundRecord = true;

                serializedItems = reader.IsDBNull(1) ? "" : reader.GetString(1);

                lockId = reader.IsDBNull(2) ? 0 : Convert.ToInt32(reader[2]);
                lockAge = DateTime.Now.Subtract(reader.GetDateTime(3));
                actionFlags = (SessionStateActions)(reader.IsDBNull(4) ? 0 : Convert.ToInt32(reader[4]));
                timeout = reader.IsDBNull(5) ? 60 : Convert.ToInt32(reader[5]);

                if (!lockRecord)
                    locked = reader.GetBoolean(7);
            }
            reader.Close();

            if (deleteData)
            {
                cmd = new OracleCommand("DELETE FROM Sessions " +
                  "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName", conn);
                cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
                cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
                cmd.ExecuteNonQuery();
            }

            if (!foundRecord)
                locked = false;

            if (foundRecord && !locked)
            {
                lockId = (int)lockId + 1;

                cmd = new OracleCommand("UPDATE Sessions SET " +
                  "LockId = :LockId, Flags = 0 " +
                  "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName", conn);
                cmd.Parameters.Add("LockId", OracleDbType.Int32).Value = lockId;
                cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
                cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
                cmd.ExecuteNonQuery();

                switch (actionFlags)
                {
                    case SessionStateActions.InitializeItem:
                        item = CreateNewStoreData(context, (int)_pConfig.Timeout.TotalMinutes);
                        break;
                    default:
                        item = Deserialize(context, serializedItems, timeout);
                        break;
                }
            }
        }
        catch (Exception e)
        {
            if (WriteExceptionsToEventLog)
            {
                WriteToEventLog(e, "GetSessionStoreItem");
                throw new ProviderException(ExceptionMessage);
            }
            else
            {
                throw new Exception(e.Message);
            }
        }
        finally
        {
            reader?.Close();
            conn.Close();
        }

        return item;
    }

    private string Serialize(SessionStateItemCollection items)
    {
        var ms = new MemoryStream();
        var writer = new BinaryWriter(ms);

        items?.Serialize(writer);

        writer.Close();

        return Convert.ToBase64String(ms.ToArray());
    }

    private SessionStateStoreData Deserialize(HttpContext context, string serializedItems, int timeout)
    {
        var ms = new MemoryStream(Convert.FromBase64String(serializedItems));
        var sessionItems = new SessionStateItemCollection();

        if (ms.Length > 0)
        {
            var reader = new BinaryReader(ms);
            sessionItems = SessionStateItemCollection.Deserialize(reader);
        }

        return new SessionStateStoreData(sessionItems, SessionStateUtility.GetSessionStaticObjects(context), timeout);
    }

    public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
    {
        var conn = new OracleConnection(_connectionString);
        var cmd = new OracleCommand("UPDATE Sessions SET Locked = 0, Expires = :Expires " +
          "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName AND LockId = :LockId", conn);
        cmd.Parameters.Add("Expires", OracleDbType.Date).Value = DateTime.Now.AddMinutes(_pConfig.Timeout.TotalMinutes);
        cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
        cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
        cmd.Parameters.Add("LockId", OracleDbType.Int32).Value = lockId;

        try
        {
            conn.Open();
            cmd.ExecuteNonQuery();
        }
        catch (Exception e)
        {
            if (WriteExceptionsToEventLog)
            {
                WriteToEventLog(e, "ReleaseItemExclusive");
                throw new ProviderException(ExceptionMessage);
            }
            else
            {
                throw new Exception(e.Message);
            }
        }
        finally
        {
            conn.Close();
        }
    }

    public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
    {
        var conn = new OracleConnection(_connectionString);
        var cmd = new OracleCommand("DELETE FROM Sessions " +
          "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName AND LockId = :LockId", conn);
        cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
        cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
        cmd.Parameters.Add("LockId", OracleDbType.Int32).Value = lockId;

        try
        {
            conn.Open();
            cmd.ExecuteNonQuery();
        }
        catch (Exception e)
        {
            if (WriteExceptionsToEventLog)
            {
                WriteToEventLog(e, "RemoveItem");
                throw new ProviderException(ExceptionMessage);
            }
            else
            {
                throw new Exception(e.Message);
            }
        }
        finally
        {
            conn.Close();
        }
    }

    public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
    {
        var conn = new OracleConnection(_connectionString);
        var cmd = new OracleCommand("INSERT INTO Sessions " +
          "(SessionId, ApplicationName, Created, Expires, " +
          "LockDate, LockId, Timeout, Locked, SessionItems, Flags, UsuarioId, Menuid) " +
          "Values(:SessionId, :ApplicationName, :Created, :Expires, :LockDate, :LockId, :Timeout, :Locked, :SessionItems, :Flags, :UsuarioId, :Menuid)", conn);
        cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
        cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
        cmd.Parameters.Add("Created", OracleDbType.Date).Value = DateTime.Now;
        cmd.Parameters.Add("Expires", OracleDbType.Date).Value = DateTime.Now.AddMinutes(timeout);
        cmd.Parameters.Add("LockDate", OracleDbType.Date).Value = DateTime.Now;
        cmd.Parameters.Add("LockId", OracleDbType.Int32).Value = 0;
        cmd.Parameters.Add("Timeout", OracleDbType.Int32).Value = timeout;
        cmd.Parameters.Add("Locked", OracleDbType.Int32).Value = Convert.ToInt32(false);
        cmd.Parameters.Add("SessionItems", OracleDbType.Varchar2).Value = "";
        cmd.Parameters.Add("Flags", OracleDbType.Int32).Value = 1;
        cmd.Parameters.Add("UsuarioId", OracleDbType.Varchar2, 50).Value = "";
        cmd.Parameters.Add("Menuid", OracleDbType.Varchar2, 50).Value = "";

        try
        {
            conn.Open();
            cmd.ExecuteNonQuery();
        }
        catch (Exception e)
        {
            if (WriteExceptionsToEventLog)
            {
                WriteToEventLog(e, "CreateUninitializedItem");
                throw new ProviderException(ExceptionMessage);
            }
            else
            {
                throw new Exception(e.Message);
            }
        }
        finally
        {
            conn.Close();
            cmd.Dispose();
        }
    }

    public override SessionStateStoreData CreateNewStoreData(HttpContext context, int i)
    {
        return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), Timeout);
    }

    public override void ResetItemTimeout(HttpContext context, string id)
    {
        var conn = new OracleConnection(_connectionString);
        var cmd = new OracleCommand("UPDATE Sessions SET Expires = :Expires " +
          "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName", conn);
        cmd.Parameters.Add("Expires", OracleDbType.Date).Value = DateTime.Now.AddMinutes(_pConfig.Timeout.TotalMinutes);
        cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
        cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;

        try
        {
            conn.Open();
            cmd.ExecuteNonQuery();
        }
        catch (Exception e)
        {
            if (WriteExceptionsToEventLog)
            {
                WriteToEventLog(e, "ResetItemTimeout");
                throw new ProviderException(ExceptionMessage);
            }
            else
            {
                throw new Exception(e.Message);
            }
        }
        finally
        {
            conn.Close();
        }
    }

    public override void InitializeRequest(HttpContext context)
    {
        if (context != null)
        {
            if (context.Request.Cookies.Count <= 0) return;

            //Verifica se a sessão é nula
            if (context.Request.Cookies["ASP.NET_SessionId"] == null) return;

            OracleDataReader reader = null;
            var conn = new OracleConnection(_connectionString); //Get connection

            try
            {
                var expireData = false;
                var id = context.Request.Cookies["ASP.NET_SessionId"].Value; //get session id cookie

                //Conexão com o banco
                conn.Open();

                //get data session oracle
                var cmd = new OracleCommand("SELECT Expires, SessionItems, LockId, LockDate, Flags, Timeout, Locked, usuarioid, menuid " + "FROM Sessions " + "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName", conn);
                cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
                cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;

                //Recupera dados da sessão da fonte de dados.
                reader = cmd.ExecuteReader(CommandBehavior.SingleRow);

                while (reader.Read())
                {
                    //Se estiver expirado marca flag true
                    DateTime expires = reader.GetDateTime(0);

                    if (expires < DateTime.Now)
                    {
                        expireData = true;
                    }
                }

                if (expireData)
                {
                    cmd = new OracleCommand("DELETE FROM Sessions " +
                                            "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName", conn);
                    cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
                    cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
                    cmd.ExecuteNonQuery();
                    context.Response.End();
                }
            }
            catch (HttpException)
            {
                //Força httpexception caso não funcione
            }
            catch (Exception e)
            {
                //Se der erro generico da classe grava log e não prossegue dando throw exception
                throw new Exception(e.Message);
            }
            finally
            {
                //Finaliza as conexões e objetos
                reader?.Close();
                conn.Close();
            }
        }
    }

    public override void EndRequest(HttpContext context)
    {
        //Não utilizado...
    }

    private static void WriteToEventLog(Exception e, string action)
    {
        //var log = new EventLog { Source = EventSource, Log = EventLog };
        if (!System.Diagnostics.EventLog.SourceExists(EventSource))
            System.Diagnostics.EventLog.CreateEventSource(EventSource, EventLog);

        var message = "Ocorreu um erro ao tentar comunicação com o banco de dados.\n\n";
        message += "Ação: " + action + "\n\n";
        message += "Exceção: " + e;

        System.Diagnostics.EventLog.WriteEntry(EventSource, message);
        //log.WriteEntry(message);
    }

    public override void Dispose()
    {
        //Não utilizado
    }

    public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
    {
        return true;
    }
}

不知道是不是这个class问题

谢谢

我发现了我的问题。在 inicializeRequest 方法中没有强制重定向到登录屏幕的操作。然后我添加了以下命令并且它起作用了。我不知道这是否是最好的方法,但它奏效了。

throw new HttpException((int)HttpStatusCode.Unauthorized, "Error Session, not authorized.");

inicializerequest 中的代码块也是如此:

    if (expireData)
{
    cmd = new OracleCommand("DELETE FROM Sessions " +
                            "WHERE SessionId = :SessionId AND ApplicationName = :ApplicationName", conn);
    cmd.Parameters.Add("SessionId", OracleDbType.Varchar2, 80).Value = id;
    cmd.Parameters.Add("ApplicationName", OracleDbType.Varchar2, 255).Value = ApplicationName;
    cmd.ExecuteNonQuery();

    throw new HttpException((int)HttpStatusCode.Unauthorized, "Error session, not authorized");
    context.Response.End();
}

我不知道这是否是最好的方法,但如果他们愿意帮助我改进我的代码,我将不胜感激。谢谢