ASP.NET页面授权...如何实现?

4

我目前正在研究替代asp.net标准页面授权配置的其他解决方案。

如果您有许多需要相同访问策略的文件目录,则位置标记可以正常工作,但是如果您有许多单独的访问策略,则位置标记会很麻烦。我可以自己编写自定义身份验证系统,但是如果可以避免这样做,那可能更好。

目前,我们正在使用类似于azman的基于权限的页面内容授权系统,但我还没有找到将其与标准页面安全性集成的好方法。

有关如何实现此操作的任何建议?是否有将azman和asp.net页面授权集成的解决方案?是否应该了解其他标准解决方案?

3个回答

6

我曾在一个有许多不同权限和角色的大型应用程序中执行以下操作[我这里没有代码,所以我会尝试在这里重新创建它]:

我首先实现了一个名为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

}

MyUserMembershipUser 的实现:


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中填写权限即可。

我不确定这是否是最佳解决方案,但我肯定它会帮助很多人 :)


我已经实现了MembershipProvider和RoleProvider。我的问题是如何将授权与各个页面解耦。我的意思是,您是否在每个页面中都放置Roles.IsInRole("admin")?您是否使用web.config位置元素?还是您有其他将其与页面解耦的方法? - JohannesH
啊,好的,那我会编辑我的回答来展示我通常的做法 :) 稍等一下。 - homemdelata
是的,"基础页面"类型的方法也是我们所采用的 - 有一个处理所有身份验证/访问控制等的基础页面和一些其他功能,然后从那个页面继承各个页面。 - Zhaph - Ben Duguid

1

你可以在数据库中将页面映射到角色,然后让主页面在页面加载时检查数据库,这样怎么样?


0
你是否在应用程序的 Application_AuthenticateRequest 方法中设置了 GenericIdentity 和 IPrincipal 对象?
我们目前使用域来进行认证,并使用 SQL Server 数据库上的用户组/角色来提供授权。在 Application_AuthenticateRequest 方法中,我会收集所有这些数据并创建基于它的 FormsAuthenticationTicket 对象。
通过这样做,我现在可以通过在代码中执行简单的 User.IsInRole("RoleX") 命令来访问用户的角色,这使得我可以轻松地锁定/解锁用户控件,或者甚至将其重定向到“授权错误”页面,如果他们没有适当的授权的话。
以下是我的 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

希望这能给你提供所需的东西,以便将其塑造成适合你的环境。

我们的应用程序中也有类似的功能,但我们只是使用自定义的角色提供程序和成员资格提供程序进行身份验证和角色加载。我真正想知道的是,您是否在每个管理页面的page_init中首先硬编码User.IsInRole("Admin")。还是您有其他解耦配置的方法?我不想在我们的页面中硬编码角色或azman规则名称。 - JohannesH
我使用web.config授权和主模板的组合来将我的实际代码检查最小化。我还有一些辅助函数,如“IsAdminAuthorized”和“IsManagerAuthorized”,当用户在几个不同的角色中符合管理员或经理资格时执行组合检查。 - Dillie-O
对我来说,这似乎有点耦合度过高。我不喜欢硬编码。基本上我认为我需要做出像 UrlAuthorization 模块一样的东西。 - JohannesH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接