Средства безопасности ASP.NET. Часть 2 - Авторизация

Источник: RSDN Magazine, #3'2004
Сергей Бакланов

Первая часть статьи была посвящена аутентификации. В ней были рассмотрены теория аутентификации, разобраны все три способа аутентификации в среде .NET Framework. Во второй части статьи будет рассмотрен ещё один аспект компьютерной безопасности - авторизация. Для начала напомним, что же такое авторизация. Авторизация - это проверка прав доступа к определённому ресурсу, или проверка привилегий на выполнение определённых действий. В качестве примера можно привести ограничение прав доступа к определенным файлам. Давайте посмотрим на вопрос авторизации не глазами пользователя, а глазами программиста, который сам диктует правила игры.

Среда ASP.NET предоставляет два способа авторизации:

  1. Авторизация доступа к файлу
  2. Авторизация доступа к URL

Авторизация доступа к файлу

Суть авторизации доступа к файлу (File Authorization) заключается в настройке политики безопасности для конкретного файла в отдельности. При этом все параметры зависят от поддерживаемых средств безопасности Windows.

При авторизации доступа к файлу применяются списки контроля доступа ACL (Access Control List), в которых содержится информация о том, кому разрешён доступ к определённому ресурсу. Список контроля доступа последовательно просматривается на совпадение текущего зарегистрировавшегося пользователя с компонентами списка, если совпадений не будет, то система вернёт сообщение о том, что нет права доступа к данному ресурсу.

В связи с тем, что авторизация доступа к файлу основывается на системных механизмах ОС, при аутентификации тоже необходимо использовать аутентификацию Windows. Для этого в файле конфигурации проекта должна присутствовать следующая строка: <authentication mode="Windows" /> Этот атрибут в проекте ASP.NET Web Application установлен по умолчанию, поэтому нет необходимости вручную вносить изменения в файл конфигурации.

Чтобы посмотреть, как действует этот механизм, создайте новый проект типа ASP.NET Web Application, переименуйте страницу ASP.NET в default.aspx и введите следующий код (листинг 1).

Листинг 1. default.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="default.aspx.vb" Inherits="AuthzByFile.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>WebForm1</title>
    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
  </head>
  <body MS_POSITIONING="GridLayout">
    <form id="Form1" method="post" runat="server">
      <p>Hello, <%= HttpContext.Current.User.Identity.Name%></p>
    </form>
  </body>
</html>

Теперь откройте Explorer и найдите этот файл (по умолчанию он должен находиться по следующему адресу: C:\Inetpub\wwwroot\<имя_проекта>\default.aspx). Далее щёлкните по нему правой кнопкой мыши и откройте окно свойств. Перейдите на вкладку Безопасность и добавьте в список допустимых имён группу администраторов (см. рисунок 1). Запустите Web-приложение, и если текущий пользователь является членом группы администраторов, то вы увидите дружелюбное приветствие, если же нет, то соответствующее сообщение об ошибке.

Рисунок 1. Установка авторизации доступа к файлу

Авторизация доступа к URL

Авторизация доступа к файлу - это старая технология, имеющая ряд недостатков: необходимость поддержки списков контроля доступа, более того, при передаче файлов по протоколу FTP списки ACL часто теряются, что зачастую приводит к нежелательным последствиям.

ПРИМЕЧАНИЕ

Сервер IIS поддерживает только авторизацию доступа к файлам, что в свою очередь сказалось на предыдущих версиях ASP, т. е. они тоже поддерживали лишь этот вид авторизации. Авторизация доступа к URL является новшеством, появившемся в ASP.NET. Кроме этого, если в настройках сервера IIS запрещен анонимный доступ, то управление авторизацией осуществляется сервером IIS, а если анонимный доступ к Web-узлу разрешён, то авторизацию уже контролирует ASP.NET.

Для использования авторизации доступа к URL сначала нужно настроить файл конфигурации проекта. За настройку авторизации отвечает тэг <authorization>, который может содержать в себе только два типа элементов:

  • <allow> - разрешает доступ к ресурсу заданным пользователям и группам пользователей.
  • <deny> - запрещает доступ к ресурсу заданным пользователям и группам пользователей.

Каждый из этих элементов поддерживает 3 атрибута:

  • users - задаёт разделённый запятыми список имён пользователей.
  • roles - задаёт разделённый запятыми список имён пользовательских групп.
  • verbs - задаёт выражения HTTP, к которым применимы тэги <allow> и <deny>. Допустимыми значениями являются: GET, HEAD, POST и *, активизирующая все вышеперечисленные режимы.

Атрибут users поддерживает два специализированных обозначения:

  • "*" - все пользователи;
  • "?" - анонимные пользователи.

По умолчанию все файлы конфигурации, включая и файл machine.config, разрешают доступ всем пользователям: <allow users="*" />. Менять значения в файле machine.config нет необходимости, поскольку при обнаружении противоречий силу имеют записи из того файла, который ближе к проекту, т. е. файла Web.config. Список может содержать множество тэгов <allow> и <deny>, и, чтобы не возникало путаницы, ASP.NET считывает параметры снизу вверх. Чтобы лучше понять, о чём идёт речь, давайте рассмотрим следующий пример:

<authorization>
  	<allow users="Steve, Mary"/>
<deny users="*" />
</authorization>

В этом фрагменте файла конфигурации мы сначала запрещаем доступ всем пользователям, а потом открываем его для двух пользователей: Steve и Mary . Кроме этого, можно свободно смешивать настройки параметров для пользователей и для групп:

<authorization>
<deny roles="BUILTIN\Guest"/>
<deny users="?"/>
<allow users="*"/>
</authorization>

В вышеприведённом листинге мы сначала открыли доступ для всех, но потом закрыли его для не зарегистрировавшихся пользователей, т. е. работающих анонимно, и для всех членов группы BUILTIN\Guests . Здесь BUILTIN обозначает коллекцию встроенных ролей, поддерживаемых ASP.NET. Чтобы просмотреть их полный список, вы можете обратиться к следующему перечислению в окне ObjectBrowser Visual Studio.NET:

System.Security.Principal.WindowsBuiltInRoles

Вместо ключевого слова BUILTIN вы также можете указывать непосредственно имя домена, на котором хранятся эти группы.

ПРИМЕЧАНИЕ

При установке прав доступа для пользователей системы, т. е. пользователей ОС, необходимо указывать в параметре users тэгов <allow> и <deny> домен, в котором они находятся; в параметре roles помимо имени домена также допустимо использование коллекции встроенных ролей BUILTIN. При этом должна быть установлена аутентификация Windows.

Для быстрой проверки работоспособности этого типа авторизации вы можете воспользоваться предыдущим примером (см. листинг 1). Для того, чтобы увидеть результат от соответствующего источника, удалите настройки авторизации доступа к файлу, т. е. отмените все установленные ограничения для файла default.aspx . После этого настройте файл конфигурации так, чтобы вашему текущему пользователю был запрещён доступ к узлу. Если всё было выполнено верно, то вы можете увидеть перед своими глазами картину, похожую на рисунок 2.

Рисунок 2. Отказано в доступе

Во всех предыдущих примерах файлов конфигурации проекта мы задавали ограничения на весь Web-узел, а часто нужно задать эти параметры лишь для определённых директорий или файлов. Для решения этой проблемы в файле конфигурации проекта ASP.NET поддерживается специальный тэг, который ограничивает распространение политики авторизации - это тэг <location>. Этот элемент имеет всего два атрибута:

  • path - задаёт путь к ресурсу, для которого будут применены параметры авторизации;
  • allowOverride - разрешает или запрещает (значения true или false соответственно) перекрывать параметры настройки тэга location данными из других файлов конфигурации.

Предположим, у нас есть проект с тремя папками, в каждой из которых содержится информация для соответствующих пользователей, и нам нужно разграничить доступ к информации между группами пользователей. Следующий фрагмент (листинг 2) файла Web.config демонстрирует применение тэга location для достижения этой цели.

Листинг 2. Разграничения прав доступа пользователей

<authorization>
<allow users="*"/>
</authorization>
    
<location path="admin_zone/admin.aspx">
  <system.web>
    <authorization>
      <allow roles="BUILTIN\Administrator"/>
      <deny users="*"/>
    </authorization>
  </system.web>
</location>
<location path="user_zone">
  <system.web>
    <authorization>
      <allow roles="BUILTIN\User, BUILTIN\Administrator"/>
      <deny users="*"/>
    </authorization>
  </system.web>
</location>
<location path="guest_zone">
  <system.web>
    <authorization>
      <allow users="?"/>
      <allow roles="BUILTIN\User, BUILTIN\Administrator"/>
      <deny users="*"/>
    </authorization>
  </system.web>
</location>

В данном примере параметры авторизации проекта настроены так, что в корневом каталоге доступ разрешён всем: предполагается, что в нём находится страница регистрации, через которую должен пройти каждый ещё не зарегистрировавшийся пользователь. Следующий тэг определяет настройки безопасности для файла admin.aspx, расположенного в папке admin_zone: к нему доступ открыт лишь членам группы Administrators. Далее указываются настройки безопасности для папки user_zone, куда разрешён доступ членам групп Users и Administrators. И, наконец, последний путь - это папка guest_zone: ко всем ресурсам, содержащимся в ней, могут обратиться анонимные пользователи и пользователи, являющиеся членами двух уже ранее названных групп - Users и Administrators.

Ролевая безопасность с применением аутентификации на основе формы

Все предыдущие примеры были ориентированы на применение средств безопасности Windows: это была и аутентификация Windows, имена пользователей и групп тоже принадлежали политике безопасности ОС. Но в некоторых случаях возникает необходимость применения в Web-проекте аутентификации на основе формы, а также выбор такого средства авторизации, которое бы не зависело от настроек безопасности сервера. И такое средство существует: вы по-прежнему продолжаете использовать аутентификацию формой, авторизацию доступа к URL, но информация о пользователях и ролях хранится не в каталоге безопасности Windows, а в любом другом месте.

Чтобы воплотить эту технологию авторизации в жизнь, давайте создадим новый Web-проект и отредактируем файл Web.config следующим образом (листинг 3).

Листинг 3. Web.config

<configuration>
<system.web>
<authentication mode="Forms">
<forms name="UrlAuth" loginUrl="login.aspx"/>
</authentication>

<authorization>
<allow users="*" />
</authorization>
</system.web>

<location path="admin.aspx">
  <system.web>
    <authorization>
      <allow roles="Administrators"/>
      <allow users="John" />
      <deny users="*"/>
    </authorization>
  </system.web>
</location>
<location path="user.aspx">
  <system.web>
    <authorization>
      <allow roles="Users, Administrators"/>
      <deny users="*"/>
    </authorization>
  </system.web>
</location>
</configuration>

Этот пример демонстрирует, что доступ к соответствующим страницам будет разрешён только членам определённых групп.

Для хранения информации о пользователях и группах мы воспользуемся базой данных SQL Server, которую мы создавали в первой части статьи. Код листинга 5 демонстрирует создание и наполнение этой базы данных.

ПРИМЕЧАНИЕ

Кроме базы данных вы можете использовать любое другое средство хранения информации: XML, INI или же обычный текстовый файл. Но преимуществом базы данных является очень гибкая и надёжная взаимосвязанная структура.

Листинг 5. FormAuthUsers2.sql

--Проверить на существование базы
IF EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name='FormAuthUsers')
  DROP DATABASE [FormAuthUsers]

--Создаём базу данных 'FormAuthUsers'
CREATE DATABASE FormAuthUsers
GO
USE FormAuthUsers
GO

--Добавляем таблицу 'Users'
CREATE TABLE [Users] (
  [ID] [int] IDENTITY (1, 1) NOT NULL,
  [UserName] [nvarchar] (50),
  [Password] [nvarchar] (50),
  
  CONSTRAINT [PK_Users] PRIMARY KEY
  ([ID]) ON [PRIMARY]
) ON [PRIMARY]
GO
--Добавляем таблицу 'Roles'
CREATE TABLE [Roles] (
  [ID] [int] IDENTITY (1,1) NOT NULL,
  [Role] [nvarchar] (50)
  
  CONSTRAINT [PK_Roles] PRIMARY KEY
  ([ID]) ON [PRIMARY]
) ON [PRIMARY]
GO
--Добавляем таблицу 'UserRole'
CREATE TABLE [UserRole] (
  [ID] [int] IDENTITY (1,1) NOT NULL,
  [UserID] [int] NOT NULL,
  [RoleID] [int] NOT NULL

  CONSTRAINT [PK_UserRole] PRIMARY KEY
  ([ID]) ON [PRIMARY]
) ON [PRIMARY]

GO

--Заполняем таблицу 'Users'
INSERT INTO Users (UserName, Password)
VALUES('John', 'one')
GO
INSERT INTO Users (UserName, Password)
VALUES('Mike', 'two')
GO
INSERT INTO Users (UserName, Password)
VALUES('Bill', 'three')
GO
-- Заполняем таблицу 'Roles'
INSERT INTO Roles(Role)
VALUES('Administrators')
GO
INSERT INTO Roles(Role)
VALUES('Users')
GO
--Заполняем таблицу 'UserRole'
INSERT INTO UserRole(UserID, RoleID)
VALUES (1, 1)
GO
INSERT INTO UserRole(UserID, RoleID)
VALUES (2, 1)
GO
INSERT INTO UserRole(UserID, RoleID)
VALUES (3, 2)

GO

--Создаём процедуру 'FindUser'
CREATE PROCEDURE FindUser
  @Name nvarchar(50),
  @Password nvarchar(50)
AS
  SELECT COUNT(ID) FROM Users
  WHERE UserName = @Name AND Password = @Password

GO

--Создаём процедуру 'FindRoles'
CREATE PROCEDURE FindRoles
  @UserName nvarchar (50)
AS
  SELECT R.Role FROM Users U, Roles R, UserRole UR
  WHERE U.UserName = @UserName AND UR.UserID = U.ID AND UR.RoleID = R.ID
GO

Теперь, когда база данных с информацией о пользователях и группах готова, и политика доступа к ресурсам налажена, можно приступить непосредственно к созданию страниц, которые будут работать со всеми этими атрибутами. Но сначала давайте проведём короткий инструктаж по дальнейшим действиям: когда мы работали с системой безопасности Windows, все внутренние процессы, обеспечивающие эффективную работу, проходили без нашего вмешательства - о них заботились ASP.NET и Windows. При использовании аутентификации на основе формы такой "фокус" не пройдёт: нам нужно будет вручную создать определённые опознавательные знаки для ASP.NET, чтобы система знала, к какой роли принадлежит текущий пользователь. Для этого мы создадим билет аутентификации на основе формы , после чего информацию, содержащуюся в этом билете, нужно будет зашифровать и сохранить в cookie-файле. В результате, после прохождения аутентификации и авторизации на машине клиента появятся два cookie-файла. Чтобы увидеть всё собственными глазами, добавьте в ваш Web-проект новую страницу и назовите её login.aspx (листинг 6).

Листинг 6 login.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="login.aspx.vb" Inherits="AuthzByUrl.login"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>REgistration page</title>
    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
    <meta name=vs_defaultClientScript content="JavaScript">
    <meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
  </head>
  <body MS_POSITIONING="GridLayout">

    <form id="Form1" method="post" runat="server">
    <table border>
      <tr>
        <th>Name:</th>
        <td><asp:TextBox ID="txtName" Runat=server /></td>
      </tr>
      <tr>
        <th>Password:</th>
        <td><asp:TextBox ID="txtPassword" Runat=server TextMode=Password /></td>
      </tr>
      <tr>
        <td><asp:Label ID="lbl" Runat=server ForeColor=Maroon Visible=False Font-Bold=True>
        Wrong data!!!</asp:Label></td>
        <td><asp:Button ID="btnLogin" Runat=server /></td>
      </tr>
    </table>
    </form>
  </body>
</html>

login.aspx.vb:

Imports System.Data.SqlClient
Imports System.Web.Security

Public Class login
    Inherits System.Web.UI.Page

    Protected WithEvents txtName As System.Web.UI.WebControls.TextBox
    Protected WithEvents txtPassword As System.Web.UI.WebControls.TextBox
    Protected WithEvents lbl As System.Web.UI.WebControls.Label
    Protected WithEvents btnLogin As System.Web.UI.WebControls.Button

#Region " Web Form Designer Generated Code "

    'This call is required by the Web Form Designer.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

    End Sub

    'NOTE: The following placeholder declaration is required by the Web Form Designer.
    'Do not delete or move it.
    Private designerPlaceholderDeclaration As System.Object

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        'CODEGEN: This method call is required by the Web Form Designer
        'Do not modify it using the code editor.
        InitializeComponent()
    End Sub

#End Region

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'Put user code to initialize the page here
    End Sub

    Private Sub btnLogin_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnLogin.Click
        Dim cn As New SqlConnection("server=localhost;database=FormAuthUsers;uid=sa;pwd=;")
        Dim cm As New SqlCommand("FindUser", cn)
        Dim dr As SqlDataReader
        Dim ticket As FormsAuthenticationTicket

        Dim n As Integer, strRoles As String, strEncrypted As String


        ' Открываем соединение
        Try
            cn.Open()
        Catch ex As SqlException
            Response.Write(ex.Message)
            Exit Sub
        End Try

        ' Задаём тип команды
        cm.CommandType = CommandType.StoredProcedure

        ' Добавляем параметры имени
        Dim prmName = New SqlParameter("@Name", SqlDbType.NVarChar, 50)
        prmName.Value = txtName.Text
        cm.Parameters.Add(prmName)
        ' Добавляем параметр пароля
        Dim prmPass = New SqlParameter("@Password", SqlDbType.NVarChar, 50)
        prmPass.Value = txtPassword.Text
        cm.Parameters.Add(prmPass)

        ' Выполняем запрос
        n = cm.ExecuteScalar

        If n > 0 Then
            ' Если пользователь с таким именем и паролем существует, то ищем его роли
            cm = Nothing
            cm = New SqlCommand("exec FindRoles '" & txtName.Text & "'", cn)
            dr = cm.ExecuteReader()
            ' Составляем список ролей
            While dr.Read
                If strRoles = "" Then
                    strRoles &= dr(0)
                Else
                    strRoles &= ", " & dr(0)
                End If
            End While
            ' Создаём аутентификационный билет
            ticket = New FormsAuthenticationTicket(1, txtName.Text, DateTime.Now, _
                 DateTime.Now.AddMinutes(20), False, strRoles)
            ' Шифруем билет
            strEncrypted = FormsAuthentication.Encrypt(ticket)
            ' Сохраняем cookie-файл
            Response.Cookies.Add(New HttpCookie("UrlAuthz", strEncrypted))

            ' Возвращаемся на исходную страницу
            FormsAuthentication.RedirectFromLoginPage(txtName.Text, False)
        Else
            ' Если пользователь не был найден, то выдаём сообщение об ошибке
            lbl.Visible = True
        End If
    End Sub
End Class

В этом примере мы поместили в одну процедуру две операции проверки: одна - аутентификации, другая - авторизации. Сначала мы проходим аутентификацию, запрашивая данные на пользователя с таким-то именем и паролем из базы. Если пользователь не был найден, выводим соответствующее сообщение об ошибке (см. 4 строку снизу). Если же пользователь обнаружен, то мы определяем его роли, опять запрашивая информацию из базы данных. На основе полученных сведений о ролях формируется аутентификационный билет, который впоследствии шифруется и сохраняется в cookie-файле. И, наконец, пользователь благополучно возвращается на страницу default.aspx.

Поскольку в нашем файле конфигурации были прописаны ограничения доступа для нескольких файлов, то давайте разберём их содержимое (листинг 7).

Листинг 7. default.aspx

<%@ Page Language="vb" AutoEventWireup="false" Inherits="AuthzByUrl.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
  <HEAD>
    <title>AuthzByUrl</title>
    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
  </HEAD>
  
  <script language="vb" runat=server >
    Sub Page_Load(sender As Object, e As EventArgs) Handles MyBase.Load
      If HttpContext.Current.User.Identity.Name = "" Then
        lblLogin.Text = "You're not registered, please login"
      Else
        lblLogin.Text = "You're registered as " & _
          HttpContext.Current.User.Identity.Name
      End If
    End Sub
    
    Sub btnLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
      Response.Redirect("login.aspx")
    End Sub
  </script>
  
  <body MS_POSITIONING="GridLayout">
    <form id="Form1" method="post" runat="server">
      <asp:Label ID="lblLogin" ForeColor="Maroon" Runat="server" Font-Bold=True>
        You're not registered, please login</asp:Label>
      <asp:Button ID="btnLogin" Runat="server" OnClick="btnLogin_Click"
        Text="Login"/><br>
      <br>
      <b>GoTo:</b>
      <ul>
        <li>
          <a href="admin.aspx">Admin zone</a>
        <li>
          <a href="user.aspx">User zone</a></li>
      </ul>
    </form>
  </body>
</HTML>

admin.aspx

<%@ Page Language="vb" AutoEventWireup="false" Inherits="AuthzByUrl.admin"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>admin</title>
    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
    <meta name=vs_defaultClientScript content="JavaScript">
    <meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
  </head>
  <body MS_POSITIONING="GridLayout">

    <form id="Form1" method="post" runat="server">
    <%
      Response.Write("<b>Hello " & HttpContext.Current.User.Identity.Name & "!</b><br>")
      Response.Write("You're admin<br><a href='default.aspx'>Go back</a>")
    %>
    </form>
  </body>
</html>

user.aspx

<%@ Page Language="vb" AutoEventWireup="false" Inherits="AuthzByUrl.user"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
  <head>
    <title>user</title>
    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
    <meta name=vs_defaultClientScript content="JavaScript">
    <meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5">
  </head>
  <body MS_POSITIONING="GridLayout">

    <form id="Form1" method="post" runat="server">
    <%
      Response.Write("<b>Hello " & HttpContext.Current.User.Identity.Name & "!</b><br>")
      Response.Write("You're user<br><a href='default.aspx'>Go back</a>")
    %>
    </form>

  </body>
</html>

После создания этого простого Web-узла вы сможете собственными глазами увидеть плоды своего труда. В приведённом коде указаны все инструкции, необходимые для создания действующей системы безопасности для сайта на базе аутентификации формой и авторизации с помощью URL.

Заимствование полномочий

Заимствование полномочий - это такой режим работы, при котором приложение ASP.NET функционирует от имени конкретного пользователя. Казалось бы, какой смыл вводить заимствование полномочий, если при аутентификации Windows пользователь и так заходит под конкретной учётной записью? Но всё дело в том, что идентификатор пользователя при аутентификации и идентификатор пользователя при заимствовании полномочий - это разные вещи, и применяются они соответственно для получения различной информации.

По умолчанию режим заимствования полномочий в среде ASP.NET отключён. Для его активизации нужно добавить в файл Web.config тэг <identity> и присвоить его атрибуту impersonate значение true. Следующий фрагмент файла конфигурации проекта демонстрирует, как это должно выглядеть:

Web.config

<identity impersonate="true"/>
 
<authentication mode="Windows" /> 
 
<authorization>
    <allow users="*" />
</authorization>

Для демонстрации работы этого режима, используйте следующий код (листинг 8) в странице default.aspx:

default.aspx

<%@ Page Language="vb" AutoEventWireup="false" Codebehind="default.aspx.vb" Inherits="Impersonation.WebForm1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
  <HEAD>
    <title>Impersonation</title>
    <meta name="GENERATOR" content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" content="Visual Basic .NET 7.1">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
  </HEAD>
  <body MS_POSITIONING="GridLayout">
    <form id="Form1" method="post" runat="server">
      <table border>
        <tr>
          <th colspan=2 bgcolor=Silver align=left><i>User:</i></th>
        </tr>
        <tr>
          <th>IsAuthenticated</th>
          <td id="clmIsAuthU" runat=server></td>
        </tr>
        <tr>
          <th>Authentication type</th>
          <td id="clmAuthTypeU" runat=server></td>
        </tr>
        <tr>
          <th>Name</th>
          <td id="clmNameU" runat=server></td>
        </tr>
        
        <tr>
          <th colspan=2 bgcolor=silver align=left><i>WindowsIdentity:</i></th>
        </tr>
        <tr>
          <th>IsAuthenticated</th>
          <td id="clmIsAuthW" runat=server></td>
        </tr>
        <tr>
          <th>Authentication type</th>
          <td id="clmAuthTypeW" runat=server></td>
        </tr>
        <tr>
          <th>Name</th>
          <td id="clmNameW" runat=server></td>
        </tr>
      </table>
    </form>
  </body>
</HTML>

default.aspx.vb

Imports System.Security.Principal

Public Class WebForm1
    Inherits System.Web.UI.Page

#Region " Web Form Designer Generated Code "

    'This call is required by the Web Form Designer.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

    End Sub

    'NOTE: The following placeholder declaration is required by the Web Form Designer.
    'Do not delete or move it.
    Private designerPlaceholderDeclaration As System.Object

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        'CODEGEN: This method call is required by the Web Form Designer
        'Do not modify it using the code editor.
        InitializeComponent()
    End Sub

#End Region

    Protected WithEvents clmIsAuthU As System.Web.UI.HtmlControls.HtmlTableCell
    Protected WithEvents clmAuthTypeU As System.Web.UI.HtmlControls.HtmlTableCell
    Protected WithEvents clmNameU As System.Web.UI.HtmlControls.HtmlTableCell
    Protected WithEvents clmIsAuthW As System.Web.UI.HtmlControls.HtmlTableCell
    Protected WithEvents clmAuthTypeW As System.Web.UI.HtmlControls.HtmlTableCell
    Protected WithEvents clmNameW As System.Web.UI.HtmlControls.HtmlTableCell

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim wi As WindowsIdentity

        ' User.Identity
        With context.User.Identity
            clmIsAuthU.InnerText = .IsAuthenticated.ToString
            clmAuthTypeU.InnerText = .AuthenticationType.ToString
            clmNameU.InnerText = .Name
        End With

        ' System.Security.Principal.WindowsIdentity
        wi = WindowsIdentity.GetCurrent
        With wi
            clmIsAuthW.InnerText = .IsAuthenticated.ToString
            clmAuthTypeW.InnerText = .AuthenticationType.ToString
            clmNameW.InnerText = .Name
        End With
    End Sub
End Class

В обработчике события загрузки формы для получения идентификатора пользователя объекта WindowsIdentity используется метод GetCurrent, возвращающий идентификатор учётной записи, от имени которой функционирует процесс ASP.NET.

При запуске этого приложения с отключенным заимствованием полномочий (<identity impersonate="false"/>) перед вами появится экран, представленный на рисунке 3. Как можно наблюдать, при отключенном заимствовании полномочий в объекте WindowsIdentity содержится идентификатор системного пользователя ASPNET.

ПРИМЕЧАНИЕ

Проследите, чтобы в этом примере анонимный доступ был запрещён

Рисунок 3. Запрещённые заимствование полномочий и анонимный доступ

Теперь, если вы активизируете заимствование полномочий, то увидите результат, представленный в таблице 1.

Таблица 1. Включенное заимствование полномочий и отключенный анонимный доступ

User:

IsAuthenticated True
Authentication type Negotiate
Name BIGDRAGON\B@k$

WindowsIdentity:

IsAuthenticated True
Authentication type NTLM
Name BIGDRAGON\B@k$

Как видите, результаты одинаковые, поскольку оба объекта получают информацию о текущем пользователе. Но предыдущие два примера были ориентированы на условия с запрещённым анонимным доступом для аутентификации средствами Windows. Если разрешить анонимный доступ к приложению, то объект User.Identity не вернёт никакого имени пользователя, а его свойство IsAuthenticated будет иметь значение False. В этом нет ничего удивительного, т. к. если в системе аутентификации Windows разрешён анонимный доступ, то пользователь работает анонимно, то есть не проходит аутентификацию.

ПРИМЕЧАНИЕ

Чтобы разрешить анонимный доступ, найдите в IIS ваше приложение и откройте его окно свойств, перейдите во вкладку Безопасность каталога и в рамке, содержащей настройки анонимного доступа, нажмите Изменить . В появившемся окне активизируйте переключатель Анонимный доступ .

В это же время у объекта WindowsIdentity свойство IsAuthenticated будет иметь значение True, а в качестве имени пользователя будет стоять строка следующего формата: IUSR_ <ИмяМашины> , как показано в таблице 2.

Таблица 2. Заимствование полномочий и анонимный доступ разрешены

User:

IsAuthenticated False
Authentication type  
Name  

WindowsIdentity:

IsAuthenticated True
Authentication type NTLM
Name BIGDRAGON\IUSR_BIGDRAGON

Свойство name объекта WindowsIdentity имеет такое значение потому, что оно возвращает идентификатор пользователя, под которым работает процесс ASP.NET, а не пользователь Web-узла. А поскольку процесс не может работать анонимно, то он получает имя от IIS, если его невозможно получить от текущего пользователя.

Если вы были внимательны при выполнении операций по разрешению/запрету анонимного доступа, то могли заметить, что в поле Имя пользователя была как раз подставлена строка вышеуказанного формата: IUSR_ <ИмяМашины> (рис. 4).

Рисунок 4. В поле Имя пользователя содержится строка, определяющая имя процесса ASP.NET при анонимном доступе

Кроме того, в среде ASP.NET предусмотрена возможность указания, от кого именно заимствовать полномочия. С этой целью в тэге <identity> предусмотрен атрибут userName, в котором и указывается имя пользователя, у которого необходимо заимствовать полномочия.

ПРИМЕЧАНИЕ

В атрибуте userName имя пользователя указывается вместе с именем домена

В следующем фрагменте из файла Web.config, показано, как это должно выглядеть на практике:

Web.config:

<identity impersonate="true" userName="BIGDRAGON\AlBa" password=""/>
 
<authentication mode="Windows" /> 
 
<authorization>
    <allow users="*" />
</authorization>

После запуска тестового приложения с такой конфигурацией на выполнение, состояние объекта User.Identity останется неизменным, а вот в свойстве name объекта WindowsIdentity вместо строки формата IUSR_ <ИмяМашины> появится имя, указанное в атрибуте userName тэга <identity> из файла конфигурации проекта, как показано в таблице 3.

Таблица 3. Процесс ASP.NET работает от имени конкретного пользователя

User:

 
IsAuthenticated False
Authentication type  
Name  

WindowsIdentity:

 
IsAuthenticated True
Authentication type NTLM
Name BIGDRAGON\AlBa

Если вы отмените анонимный доступ, то объект User.Identity будет содержать идентификатор зарегистрированного пользователя, а в объекте WindowsIdentity по-прежнему будет оставаться имя пользователя, переданное через атрибут userName.

На этом закончим изучение авторизации как средства безовасности среды ASP.NET. Дальнейшее изучение механизма авторизации требует изучения средств авторизации Windows. Среди них можно выделить списки контроля доступа на низком и высоком уровне, контроль доступа архитектуры клиент/сервер, ролевая безопасность Windows и так далее.

Если эта тема вас действительно заинтересовала, то вы можете найти массу материала в библиотеке MSDN:

  • Темы безопасности в рамках ASP.NET доступны в следующей ветке библиотеки MSDN: .NET Development/.NET Security;
  • По вопросам безопасности всей системы в целом следует обращаться к разделу Security/Security (General)/SDK Documentation.

Если у вас нет MSDN-библиотеки, то к её самому свежему изданию можно обратиться через интернет по адресу: http://msdn.microsoft.com/library/.

В третьей, заключительной части данной статьи, мы рассмотрим очень актуальную и интересную тему - криптографию. Кроме теории и алгоритмов криптографии мы рассмотрим с различных сторон средства шифрования, предоставляемые платформой .NET Framework, и создадим простенький метод шифрования.


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=5197