Опубликован: 07.05.2010 | Доступ: свободный | Студентов: 1678 / 62 | Оценка: 4.56 / 4.06 | Длительность: 34:11:00
Лекция 10:

Управление состоянием ASP.NET

Упражнение 4

  • Добавьте к проекту новую страницу (без файла отделенного кода) с именем DisplayViewState.aspx и установите ее стартовой
  • Удалите из интерфейсной части страницы дескриптор <div></div>
  • Включите трассировку, добавив в директиву @ Page страницы параметр Trace=true
  • Добавьте в блок скриптов кодовую часть, чтобы окончательно страница стала такой
    <%@ Page Language="C#" Trace="true" %>
        
    <script runat="server">
        
        protected void Page_Load(object sender, EventArgs e)
        {
            // Объекты создаем каждый раз заново
            Label label = new Label();
            this.form1.Controls.Add(label);
            
            this.form1.Controls.Add(new HtmlGenericControl("br"));
            
            Button button = new Button();
            this.form1.Controls.Add(button);
            
            if (!this.IsPostBack)
            {
                // Один раз настроили, а дальше работает состояние вида
                label.Text = "Привет всем!!!";
                button.Text = "Отправить";
            }
        }
        
        protected void Page_PreRender(object sender, EventArgs e)
        {
            // Вывод на страницу строки состояния
            if (this.IsPostBack)
            {
                // Извлекаем строку состояния
                string strViewState = this.Request.Form["__VIEWSTATE"];
        
                // Добавляем вывод к родительской форме 
                Label label = new Label();
                this.form1.Controls.Add(label);
                label.Text = "<p><b>Строка состояния как есть:</b><br />" 
                    + strViewState + "</p>";
                label.EnableViewState = false;// Чтобы не влияла на результат
        
                // Преобразуем строку из Base64 в массив ASCII-символов
                byte[] ascii = Convert.FromBase64String(strViewState);
        
                // Вставляем в отклик
                Response.Write("<b>Строка состояния в формате ASCII:</b><br />");
                Response.BinaryWrite(ascii);
        
                // То же самое, только по турецки...
                // Десериализация и отображение строки
                //string decodedViewState = System.Text.Encoding.ASCII.GetString(ascii);
                //Response.Write(decodedViewState);
            }
        }
    </script>
        
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        </form>
    </body>
    </html>
  • Запустите страницу DisplayViewState.aspx и повыполните обратную отсылку

HTML-отклик на стороне клиента, сгенерированный страницей DisplayViewState.aspx при обратной отсылке, будет выглядеть примерно так

Мы видим, что некоторая информация, сохраняемая на клиенте в строке состояния вида, может быть частично расшифрована. Злоумышленник может изменить ее нужным для себя образом и попытаться отправить на сервер.

Поскольку состояние вида формирует только серверная сторона, то перед включением строки в скрытое поле HTML-вывода ASP.NET вычисляет контрольную сумму и прячет ее в ту же самую строку. При получении этой хэшированной строки от клиента система десериализует ее, вновь вычисляет контрольную сумму и сравнивает с ранее спрятанной. Несовпадение легко обнаружить. Подсчет контрольной суммы включается в директиве @ Page параметром EnableViewStateMac="true".

Для большей защиты строки состояния вида можно использовать встроенное шифрование, которое включается на странице параметром ViewStateEncryptionMode="Always" директивы @ Page или установкой в конфигурационном файле секции

<pages viewStateEncryptionMode="Always">
  • Включите шифрование и выполните страницу DisplayViewState.aspx. Сгенерированный отклик на обратную отсылку станет примерно таким

Теперь строка состояния стала зашифрованной и ее трудно расшифровать. Однако не рекомендуется без надобности шифровать данные состояния вида. Это негативно скажется на производительности, поскольку сервер должен будет выполнять операцию шифрования и дешифрования при каждой отсылке и получении данных.

Скрытые поля

Во вкладке Standard панели Toolbox имеется элемент управления HiddenField, который генерирует скрытое поле c единственной переменной, определяемой его свойством Value. Этот элемент не поддерживает шифрование, хэширование и хранение по частям. Поэтому клиенты в исходном коде HTML-вывода могут просматривать эти данные. Данные в скрытом поле хранятся до тех пор, пока пользователь не перейдет к другой странице.

Упражнение 5

  • Добавьте к проекту новую страницу с именем HiddenFieldTest.aspx без файла отделенного кода и назначьте ее стартовой
  • Переведите страницу в режим Design и двойным щелчком создайте в кодовой части обработчик Page_Load()
  • Поместите на Web-форму из вкладки Standard панели Toolbox два элемента управления HiddenField и присвойте им имена beginTime и countRequest
  • Отключите для всех элементов страницы механизм сохранения состояния вида, добавив в директиву @ Page параметр EnableViewState="false"
  • Заполните страницу следующим кодом
    <%@ Page Language="C#" EnableViewState="false" %>
        
    <script runat="server">
        
        protected void Page_Load(object sender, EventArgs e)
        {
            // Создаем текстовую метку
            lblTime = new Label();
            div1.Controls.Add(lblTime);
            div1.Controls.Add(new HtmlGenericControl("br"));
        
            // Создаем кнопку CurrentTime
            Button button = new Button();
            div1.Controls.Add(button);
            // Подписываемся на событие кнопки
            button.Click += CurrentTimeClick;
            button.Text = "Текущее время";
            
            // Добавляем промежуток между кнопками
            Literal literal = new Literal();
            literal.Text = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
            div1.Controls.Add(literal);
        
            // Создаем кнопку OldTime
            button = new Button();
            div1.Controls.Add(button);
            // Подписываемся на событие кнопки
            button.Click += OldTimeClick;
            button.Text = "Начальное время";
        
            // Инициализируем скрытые поля и метку при первом запросе
            if (!this.IsPostBack)
            {
                beginTime.Value = System.DateTime.Now.ToLongTimeString();
                OldTimeClick(null, EventArgs.Empty);
            }
            else
            {
                // Ведение счетчика запросов страницы 
                int.TryParse(countRequest.Value, out count);
                count++;
                // Или то же самое
                // count = int.Parse(countRequest.Value) + 1;
            }
            
            countRequest.Value = count.ToString();
        }
        
        // Вынесены в поля класса для видимости в обработчиках
        int count = 0;
        Label lblTime;
        
        void CurrentTimeClick(object sender, EventArgs e)
        {
            lblTime.Text = "Текущее время: " + System.DateTime.Now.ToLongTimeString();
            lblTime.Text += "<br />Ответ № " + count.ToString();
        }
        
        void OldTimeClick(object sender, EventArgs e)
        {
            lblTime.Text = "Начальное время: " + beginTime.Value;
            lblTime.Text += "<br />Ответ № " + count.ToString();
        }
    </script>
        
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
            <div style="text-align: center" id="div1" runat="server">
                <h1 style="color: Red">
                    Испытание элемента HiddenField
                </h1>
                <asp:HiddenField ID="beginTime" runat="server" />
                <asp:HiddenField ID="countRequest" runat="server" />
            </div>
        </form>
    </body>
    </html>
  • Исполните страницу и убедитесь, что скрытые поля, генерируемые элементом HiddenField, в пределах одной страницы работают исправно