创建 AuthorizeAttribute - 我需要知道什么?

发布于 2024-12-26 11:48:13 字数 1078 浏览 2 评论 0原文

以下是我的要求:

  • 我将向 N 个角色添加用户;在数据库中定义。

  • 我需要使用我的授权属性保护每个控制器操作。

例如,Web 应用程序将检查登录的用户是否属于这两个角色中的任何一个,如果属于,我就让他们进入。如何告诉 Authorize 属性从数据库表中获取用户角色选择?

 [Authorize(Roles = "Admin, Technician")]
 public ActionResult Edit(int id)
 {
     return View();
 }

我已经尝试过用谷歌搜索许多不同的页面,但似乎没有一个适合我的需要,而且过于复杂。

如果官方文档有一些我'我也很想找到它,因为我没有看到任何可以使用的东西。

有什么建议吗?

例如,这个问题的答案看起来很干净,但我不知道它是否完整或遗漏了一些重要的东西。

ASP.NET MVC3 角色和权限管理 -> ;使用运行时权限分配


编辑

看来我实际上正在寻找的是创建一个自定义角色提供程序,对吗?我需要实现此类并将其用作我的角色提供程序吗?我对此还很陌生,有什么想法吗?

http://msdn.microsoft.com/en-us/library/8fw7xh74。 ASPX

Here are my requirements:

  • I'll be adding users to N amount of roles; defined in a database.

  • I need to protect each controller action with my authorize attribute.

For example, the web application would check if the logged in user belongs to either of these two roles and if they do, I let them in. How can I tell the Authorize attribute to fetch the user roles from a database table I choose?

 [Authorize(Roles = "Admin, Technician")]
 public ActionResult Edit(int id)
 {
     return View();
 }

I've tried Googling for many different pages, but none seem to fit in with what I need and are overly complicated.

If the official documentation has something I'd love to find it as well, as I didn't see anything I could use.

Any suggestions?

For example, this question has a very clean looking answer, but I don't know if it's complete or missing something important.

ASP.NET MVC3 Role and Permission Management -> With Runtime Permission Assignment


Edit

It seems that what I'm actually looking for is creating a custom role provider, correct? Do I need to implement this class and use it as my Role provider? I'm fairly new at this, any thoughts?

http://msdn.microsoft.com/en-us/library/8fw7xh74.aspx

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

救星 2025-01-02 11:48:13

在过去的几周里,我经历了几乎相同的情况,所以这可能会对处于同一条船上的其他人有所帮助。我的场景是公司 Intranet 上的 MVC4 应用程序,用户存储在 Active Directory 中。这允许 Windows 身份验证提供单点登录,因此不需要表单身份验证。角色存储在 Oracle 数据库中。我有 3 个角色:

  • 只读:所有用户都需要成为该角色的成员才能访问应用程序
  • 用户:创建新记录
  • 管理员:编辑和删除记录

我决定使用 asp.net 角色提供程序 api 来创建我自己的 AccountRoleProvider。到目前为止,我只需要使用 2 个方法,GetRolesForUser 和 IsUserInRole:

public class AccountRoleProvider : RoleProvider // System.Web.Security.RoleProvider
{
    private readonly IAccountRepository _accountRepository;

    public AccountRoleProvider(IAccountRepository accountRepository)
    {
        this._accountRepository = accountRepository;
    }

    public AccountRoleProvider() : this (new AccountRepository())
    {}

    public override string[] GetRolesForUser(string user521)
    {
        var userRoles = this._accountRepository.GetRoles(user521).ToArray();

        return userRoles;
    }

    public override bool IsUserInRole(string username, string roleName)
    {
        var userRoles = this.GetRolesForUser(username);

        return Utils.IndexOfString(userRoles, roleName) >= 0;
    }
}

我更新了 web.config 以使用我的角色提供程序:

<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="AccountRoleProvider">
  <providers>
    <clear/>
    <add name="AccountRoleProvider"
         type="MyApp.Infrastructure.AccountRoleProvider" />
  </providers>
</roleManager>

然后我从 AuthorizeAttribute、ReadOnlyAuthorize 和 CustomAuthorize 创建了 2 个自定义属性。

ReadonlyAuthorize:

public class ReadonlyAuthorize : AuthorizeAttribute
{
    private IAccountRepository _accountRepository;

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var user = httpContext.User;
        this._accountRepository = new AccountRepository();

        if (!user.Identity.IsAuthenticated)
        {
            return false;
        }

        // Get roles for current user
        var roles = this._accountRepository.GetRoles(user.Identity.Name);

        if (!roles.Contains("readonly"))
        {
            return false;
        }

        return base.AuthorizeCore(httpContext);
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
        {
            filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
        }
    }
}

CustomAuthorize:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public string RedirectActionName { get; set; }
    public string RedirectControllerName { get; set; }
    private IAccountRepository _accountRepository;

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var user = httpContext.User;
        this._accountRepository = new AccountRepository();
        var accessAllowed = false;

        // Get the roles passed in with the (Roles = "...") on the attribute
        var allowedRoles = this.Roles.Split(',');

        if (!user.Identity.IsAuthenticated)
        {
            return false;
        }

        // Get roles for current user
        var roles = this._accountRepository.GetRoles(user.Identity.Name);

        foreach (var allowedRole in allowedRoles)
        {
            if (roles.Contains(allowedRole))
            {
                accessAllowed = true;
            }
        }

        if (!accessAllowed)
        {
            return false;
        }

        return base.AuthorizeCore(httpContext);
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
        {   
            var values = new RouteValueDictionary(new
            {
                action = this.RedirectActionName == string.Empty ? "AccessDenied" : this.RedirectActionName,
                controller = this.RedirectControllerName == string.Empty ? "Home" : this.RedirectControllerName
            });

            filterContext.Result = new RedirectToRouteResult(values);
        }
    }
}

使用 2 个不同属性的原因是我使用一个属性作为 Readonly 角色,所有用户都必须是该角色的成员才能访问该应用程序。我可以将其添加到 Global.asax 中的 RegisterGlobalFilters 方法中,这意味着它会自动应用于每个控制器:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new ReadonlyAuthorize());
}

然后在 CustomAuthorize 中,我可以采取更精细的方法并指定我想要的角色并应用于控制器或单个操作,例如下面我可以将对删除方法的访问限制为管理员角色的用户:

[AccessDeniedAuthorize(RedirectActionName = "AccessDenied", RedirectControllerName = "Home", Roles = "Admin")]
public ActionResult Delete(int id = 0)
{
    var batch = myDBContext.Batches.Find(id);
    if (batch == null)
    {
        return HttpNotFound();
    }

    return View(batch);
}

我需要采取进一步的步骤,例如使用当前用户所属的角色更新用户对象。这将在我的自定义属性中检索一次用户的角色,而不是每次,并且还利用 User.IsInRole。 Gloal.asax 中的 Application_AuthenticateRequest 中应该可以实现类似的操作:

var roles = "get roles for this user from respository";

if (Context.User != null)
    Context.User = new GenericPrincipal(Context.User.Identity, roles);

I've been going through pretty much the same scenario the past couple of weeks so this might help someone else in the same boat. My scenario is an MVC4 application on a company intranet with users stored in Active Directory. This allows for Windows authentication giving single sign-on so no need for Forms authentication. Roles are stored in an Oracle database. I have 3 roles:

  • Readonly: All users need to be a member of this to access the application
  • User: Create new resords
  • Admin: Edit and delete records

I decided to use the asp.net role provider api to create my own AccountRoleProvider. So far I only need to use 2 methods in this, GetRolesForUser and IsUserInRole:

public class AccountRoleProvider : RoleProvider // System.Web.Security.RoleProvider
{
    private readonly IAccountRepository _accountRepository;

    public AccountRoleProvider(IAccountRepository accountRepository)
    {
        this._accountRepository = accountRepository;
    }

    public AccountRoleProvider() : this (new AccountRepository())
    {}

    public override string[] GetRolesForUser(string user521)
    {
        var userRoles = this._accountRepository.GetRoles(user521).ToArray();

        return userRoles;
    }

    public override bool IsUserInRole(string username, string roleName)
    {
        var userRoles = this.GetRolesForUser(username);

        return Utils.IndexOfString(userRoles, roleName) >= 0;
    }
}

I updated the web.config to use my role provider:

<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="AccountRoleProvider">
  <providers>
    <clear/>
    <add name="AccountRoleProvider"
         type="MyApp.Infrastructure.AccountRoleProvider" />
  </providers>
</roleManager>

Then I created 2 custom attributes from AuthorizeAttribute, ReadOnlyAuthorize and CustomAuthorize.

ReadonlyAuthorize:

public class ReadonlyAuthorize : AuthorizeAttribute
{
    private IAccountRepository _accountRepository;

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var user = httpContext.User;
        this._accountRepository = new AccountRepository();

        if (!user.Identity.IsAuthenticated)
        {
            return false;
        }

        // Get roles for current user
        var roles = this._accountRepository.GetRoles(user.Identity.Name);

        if (!roles.Contains("readonly"))
        {
            return false;
        }

        return base.AuthorizeCore(httpContext);
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
        {
            filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
        }
    }
}

CustomAuthorize:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public string RedirectActionName { get; set; }
    public string RedirectControllerName { get; set; }
    private IAccountRepository _accountRepository;

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var user = httpContext.User;
        this._accountRepository = new AccountRepository();
        var accessAllowed = false;

        // Get the roles passed in with the (Roles = "...") on the attribute
        var allowedRoles = this.Roles.Split(',');

        if (!user.Identity.IsAuthenticated)
        {
            return false;
        }

        // Get roles for current user
        var roles = this._accountRepository.GetRoles(user.Identity.Name);

        foreach (var allowedRole in allowedRoles)
        {
            if (roles.Contains(allowedRole))
            {
                accessAllowed = true;
            }
        }

        if (!accessAllowed)
        {
            return false;
        }

        return base.AuthorizeCore(httpContext);
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if (filterContext.HttpContext.User.Identity.IsAuthenticated && filterContext.Result is HttpUnauthorizedResult)
        {   
            var values = new RouteValueDictionary(new
            {
                action = this.RedirectActionName == string.Empty ? "AccessDenied" : this.RedirectActionName,
                controller = this.RedirectControllerName == string.Empty ? "Home" : this.RedirectControllerName
            });

            filterContext.Result = new RedirectToRouteResult(values);
        }
    }
}

The reason for 2 different attributes is that I use one for the Readonly role that all users must be a member of in order to access the app. I can add this in the RegisterGlobalFilters method in Global.asax which means it's applied automatically to every Controller:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
    filters.Add(new ReadonlyAuthorize());
}

Then in the CustomAuthorize I can take a more granular approach and specify the roles that I want and apply to a Controller or an individual Action e.g. below I can restrict access to the Delete method to users in the Admin role:

[AccessDeniedAuthorize(RedirectActionName = "AccessDenied", RedirectControllerName = "Home", Roles = "Admin")]
public ActionResult Delete(int id = 0)
{
    var batch = myDBContext.Batches.Find(id);
    if (batch == null)
    {
        return HttpNotFound();
    }

    return View(batch);
}

There are further steps I need to take such as updating the User object with the roles the current user is a member of. This will retrieve the roles for the User once instead of every time in my custom attributes, and also utilise User.IsInRole. Something like this should be possible in Application_AuthenticateRequest in Gloal.asax:

var roles = "get roles for this user from respository";

if (Context.User != null)
    Context.User = new GenericPrincipal(Context.User.Identity, roles);
一身骄傲 2025-01-02 11:48:13

有很多方法可以处理这个问题。达林的方法和吹镖(都是非常熟练的人 - 其中一位也是安全作者)在您提供的链接中很不错。

需要注意的一件事是缓存。如果您使用服务器端输出缓存缓存,您可能会无意中为一个用户缓存一些返回给另一用户的内容。请参阅:

MVC3 中的输出缓存和授权过滤器

为什么使用 Azure 缓存(.NET MVC3 应用程序)时不能组合 [Authorize] 和 [OutputCache] 属性吗?

MVC 自定义身份验证、授权和角色实现

了解有关此内容以及如何处理缓存(如果您使用授权属性)的更多信息。

There are a bunch of ways to handle this. Darin's method and blowdarts (both very skilled individuals - one of them is a security author as well) are decent in the link you provided.

One thing to watch out for is the cache. If you use server side outputcache caching, you may inadvertently cache something for one user that gets returned to another user. Please see:

OutputCache and Authorize filters in MVC3

and

Why can't I combine [Authorize] and [OutputCache] attributes when using Azure cache (.NET MVC3 app)?

and

MVC Custom Authentication, Authorization, and Roles Implementation

for additional info on that and how to handle caching if you are using an authorize attribute.

飘落散花 2025-01-02 11:48:13

您可以使用默认的成员资格提供程序和角色提供程序,而不是实现您自己的,但您还需要使用默认的 asp.net 成员资格数据库 aspnetdb.mdf。

请参阅此处了解演练

You can use the default membership provider and role provider, rather than implement your own, but you will need to also use the default asp.net membership database, aspnetdb.mdf.

See here for a walkthrough

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文