Авторизация страницы ASP.NET Как вы это делаете?

В настоящее время я изучаю альтернативные решения для стандартной конфигурации авторизации страниц в asp.net.

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

В настоящее время мы используем систему авторизации на основе разрешений, подобную azman, для содержимого страницы, но я пока не нашел хорошего способа интеграции ее со стандартной безопасностью страницы.

Любые предложения о том, как это сделать? Существуют ли какие-либо решения, объединяющие авторизацию страниц azman и asp.net? Существуют ли какие-либо другие стандартные решения, о которых я должен знать?


person JohannesH    schedule 24.04.2009    source источник


Ответы (3)


Я сделал в огромном приложении с множеством разных разрешений и разных ролей что-то вроде следующего [у меня здесь нет кода, поэтому я просто попытаюсь воссоздать его здесь]:

Сначала я реализовал класс SecuredPage следующим образом:


public class SecuredPage : System.Web.UI.Page
{
    // Those Permissions are mandatory, so user needs to have all of them
    public List MandatoryPermissions { get; set; }

    // Those Permissions are optional, so if the user have at least one of them, he can access
    public List OptionalPermissions { get; set; }

    protected override void OnLoad(EventArgs e)
    {
        MyUser loggedUser = (MyUser) this.User;

        base.OnLoad(e);

        foreach (Permission mandatoryPermission in MandatoryPermissions)
        {
            // if the user don't have permission, we can redirect him
            if (!loggedUser.HasPermission(mandatoryPermission))
            {
                RedirectToDontHaveAccess();
                break;
            }
        }

        bool hasAccessToThePage = false;

        foreach (Permission optionalPermission in OptionalPermissions)
        {
            // If the user has at least one of the permissions, he can access
            if (loggedUser.HasPermission(optionalPermission))
            {
                hasAccessToThePage = true;
            }
        }

        if (!hasAccessToThePage)
        {
            RedirectToDontHaveAccess();
        }

    }

    private void RedirectToDontHaveAccess()
    {
        throw new NotImplementedException();
    }
}

Это будет моя базовая страница для всех страниц, для доступа к которым пользователю нужны разрешения. MandatoryPermissions — это разрешения, которые ДОЛЖНЫ быть у пользователя все для доступа к странице, а OptionalPermissions — это разрешения, которые пользователю нужны хотя бы одному из них для доступа к странице. Нет необходимости использовать оба на каждой странице, потому что если у вас есть MandatoryPermissions, не имеет значения, есть у вас дополнительные опции или нет.

Разрешение - это перечисление:


public enum Permission
{
    // Usually this enum will replicate a domain table from the database
    EditUser = 1,
    SearchUserByUsername = 2,
    SearchUserByEmail = 3

}

А MyUser — это реализация MembershipUser:


public class MyUser : System.Web.Security.MembershipUser
{
    internal bool HasPermission(Permission permission)
    {
        //
        // TODO: Check on database if the user has the permission or not
        //
    }
}

Тогда единственное, что вам нужно сделать на своих страницах, это заполнить списки разрешений:


public partial class EditUser : SecuredPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        MandatoryPermissions.Add(Permission.EditUser);
    }
}

public partial class SearchUser : SecuredPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        OptionalPermissions.Add(Permission.SearchUserByUsername);
        OptionalPermissions.Add(Permission.SearchUserByEmail);
    }
}

Хорошо, пример с поиском был не очень хорош, но я думаю, вы поняли.

Вся идея в том, что base.OnLoad(e); вызывается непосредственно перед проверкой разрешений, поэтому вам просто нужно заполнить разрешения в вашем Page_Load.

Я не уверен, что это лучшее решение, но я уверен, что это очень помогает :)

person homemdelata    schedule 25.04.2009
comment
Я реализовал MembershipProvider и RoleProvider. Мои вопросы в том, как вы отделяете авторизацию от отдельных страниц. Я имею в виду, размещаете ли вы Roles.IsInRole(admin) на каждой из своих страниц? Используете ли вы элементы местоположения web.config? Или у вас есть другой способ отделить его от страниц? - person JohannesH; 25.04.2009
comment
Ах, хорошо, поэтому я отредактирую свой ответ, чтобы показать, как я обычно это делаю :) Секунду - person homemdelata; 25.04.2009
comment
Да, подход типа базовой страницы — это то, что мы тоже делаем: у вас есть базовая страница, которая обрабатывает все аутентификацию/контроль доступа и т. д., а также несколько других функций, а затем наследует от нее отдельные страницы. - person Zhaph - Ben Duguid; 25.04.2009

Как насчет сопоставления страниц с ролями в вашей базе данных, а затем позволить вашей мастер-странице проверять БД при загрузке страницы?

person Community    schedule 08.07.2009

Настроили ли вы объекты GenericIdentity и IPrincipal во время использования метода Application_AuthenticateRequest приложения?

В настоящее время мы используем наш домен для аутентификации и групп/ролей пользователей в базе данных SQL Server для обеспечения авторизации. Во время метода Application_AuthenticateRequest я собираю все эти данные и создаю на их основе объект FormsAuthenticationTicket.

Таким образом, у меня теперь есть доступ к ролям пользователя, выполняя простую команду User.IsInRole("RoleX") в моем коде, которая позволяет мне легко блокировать/разблокировать пользовательские элементы управления или даже выполнять простой Response.Redirect() на страницу «Ошибка авторизации», если у них нет надлежащей авторизации.

Вот как выглядит мой метод AuthenticateRequest (VB.NET)

Sub Application_AuthenticateRequest(ByVal sender As Object, _
                                       ByVal e As EventArgs)

      Dim formsAuthTicket As FormsAuthenticationTicket
      Dim httpCook As HttpCookie
      Dim objGenericIdentity As GenericIdentity
      Dim objMyAppPrincipal As CustomPrincipal
      Dim strRoles As String()

      httpCook = Context.Request.Cookies.Get("authCookieEAF")
      formsAuthTicket = FormsAuthentication.Decrypt(httpCook.Value)
      objGenericIdentity = New GenericIdentity(formsAuthTicket.Name)
      strRoles = formsAuthTicket.UserData.Split("|"c)
      objMyAppPrincipal = New CustomPrincipal(objGenericIdentity, strRoles)
      HttpContext.Current.User = objMyAppPrincipal    

   End Sub

...и аналогично, вот как выглядит объект CustomPrincipal:

Public Class CustomPrincipal
   Implements IPrincipal


   ''' <summary>
   '''    Identity object of user.
   ''' </summary>
   ''' <remarks></remarks>
   Private m_identity As IIdentity

   ''' <summary>
   '''    Roles(s) a user is a part of.
   ''' </summary>
   ''' <remarks></remarks>
   Private m_roles As String()

   ''' <summary>
   '''    Name of user.
   ''' </summary>
   ''' <remarks></remarks>
   Private m_userId As String

   ''' <summary>
   '''    Gets/Sets the user name.
   ''' </summary>
   ''' <value>m_userId</value>
   ''' <returns>Current name of user.</returns>
   ''' <remarks></remarks>
   Public Property UserId() As String
      Get
         Return m_userId
      End Get
      Set(ByVal value As String)
         m_userId = value
      End Set
   End Property

   ''' <summary>
   '''    Gets the identity object of the user.
   ''' </summary>
   ''' <value>m_identity</value>
   ''' <returns>Current identity of user.</returns>
   ''' <remarks></remarks>
   Public ReadOnly Property Identity() As System.Security.Principal.IIdentity Implements System.Security.Principal.IPrincipal.Identity
      Get
         Return m_identity
      End Get
   End Property

   ''' <summary>
   '''    Full constructor.
   ''' </summary>
   ''' <param name="identity">Identity to use with Custom Principal.</param>
   ''' <param name="roles">Roles for user.</param>
   ''' <remarks>Identity object contains user name when building constructor.</remarks>
   Public Sub New(ByVal identity As IIdentity, ByVal roles As String())

      m_identity = identity
      m_roles = New String(roles.Length) {}
      roles.CopyTo(m_roles, 0)
      Array.Sort(m_roles)
      m_userId = identity.Name

   End Sub

   ''' <summary>
   '''    Determines if the current user is in the role specified.
   ''' </summary>
   ''' <param name="role">Role to test against.</param>
   ''' <returns>Boolean variable indicating if role specified exists in user's m_roles array.</returns>
   ''' <remarks></remarks>
   Public Function IsInRole(ByVal role As String) As Boolean Implements System.Security.Principal.IPrincipal.IsInRole

      Dim boolResults As Boolean

      If Array.BinarySearch(m_roles, role) >= 0 Then
         boolResults = True
      Else
         boolResults = False
      End If

      Return boolResults

   End Function

End Class

Надеюсь, это даст вам то, что вам нужно, чтобы приспособить его к вашей среде.

person Dillie-O    schedule 24.04.2009
comment
У нас есть то же самое в наших приложениях, но мы просто используем настраиваемый провайдер ролей и провайдер членства для аутентификации и загрузки ролей. Что я действительно хочу знать, так это то, что вы затем жестко запрограммируете User.IsInRole(Admin) в качестве первой вещи в page_init на каждой из ваших страниц администратора. Или у вас есть какой-то другой способ отделить эту конфигурацию? Я бы не хотел жестко запрограммировать имена ролей или правил azman на наших страницах. - person JohannesH; 25.04.2009
comment
Я использую комбинацию авторизации web.config и мастер-шаблонов, чтобы свести фактическую проверку кода к минимуму. У меня также есть пара вспомогательных функций, таких как IsAdminAuthorized и IsManagerAuthorized, которые выполняют составные проверки, когда пользователи в нескольких разных ролях квалифицируются как администратор или менеджер. - person Dillie-O; 25.04.2009
comment
Хорошо, мне это кажется слишком связанным. Я не люблю жесткое кодирование. В основном я думаю, что мне нужно сделать что-то вроде модуля UrlAuthorization. - person JohannesH; 25.04.2009