Permitir que várias funções acessem a ação do controlador

274

No momento, eu decorei um método como este para permitir que "membros" acessem minha ação do controlador

[Authorize(Roles="members")]

Como permito mais de uma função? Por exemplo, o seguinte não funciona, mas mostra o que estou tentando fazer (permitir acesso a "membros" e "administrador"):

[Authorize(Roles="members", "admin")] 
codette
fonte
4
Altere a resposta aceita para esta pergunta. A pessoa com a resposta atualmente aceita o editou, indicando que estava errado.
Eric J.

Respostas:

595

Outra opção é usar um único filtro de autorização conforme você postou, mas remova as cotações internas.

[Authorize(Roles="members,admin")]
Jim Schmehil
fonte
5
Também funciona no MVC 5. +1
gkonuralp
4
Funciona no ASP.NET Core 1.0 (MVC 6) e no Microsoft.AspNet.Identity v3. *
Soren
3
Tudo bem se você tiver apenas um controlador que você precisa autorizar. Se você tiver mais de um, duplicará essas constantes de sequência (eca). Eu prefiro muito a classe estática que possui os nomes das funções. Meu ódio por animais de estimação são seqüências de caracteres duplicadas ... tão ruins.
robnick
1
boas notícias @kraeg que você resolveu seu problema. Agora, considere excluir seus comentários, por favor
Pablo Claus
1
Por quê? Levei anos para resolver isso. Pode ser útil para outra pessoa com o mesmo problema.
kraeg 21/06/19
129

Se você deseja usar funções personalizadas, pode fazer o seguinte:

CustomRoles classe:

public static class CustomRoles
{
    public const string Administrator = "Administrador";
    public const string User = "Usuario";
}

Uso

[Authorize(Roles = CustomRoles.Administrator +","+ CustomRoles.User)]

Se você tem poucos papéis, talvez você possa combiná-los (para maior clareza) como este:

public static class CustomRoles
{
     public const string Administrator = "Administrador";
     public const string User = "Usuario";
     public const string AdministratorOrUser = Administrator + "," + User;  
}

Uso

[Authorize(Roles = CustomRoles.AdministratorOrUser)]
Pablo Claus
fonte
7
Essa seria uma boa resposta, se você explicasse às pessoas que não sabiam o que está por trás dos CustomRoles.
James Skemp
1
@JamesSkemp ok, eu estendi minha resposta. É muito simples. CustumRoles é uma classe que eu criei que contém algumas constantes, que corresponde às minhas funções de aplicativo. Fiz isso por alguns motivos: 1) Permitiu o uso do intellisense para evitar erros de ortografia 2) Para simplificar a manutenção. Se uma função mudar, preciso atualizar apenas um local no meu aplicativo.
Pablo Claus
@Pabloker Como alternativa, você pode criar uma enumeração com um atributo Flags, por exemplo, Convert.ToString (CustomRoles.Administrator | CustomRoles.User); - parte chata é que isso requer uma conversão explícita
cstruter
Se você tem 39 papéis?
Kiquenet
Eu acho que o problema passa pela modelagem de licenças além do que pode ser feito com .net
Pablo Claus
82

Uma possível simplificação seria a subclasse AuthorizeAttribute:

public class RolesAttribute : AuthorizeAttribute
{
    public RolesAttribute(params string[] roles)
    {
        Roles = String.Join(",", roles);
    }
}

Uso:

[Roles("members", "admin")]

Semanticamente, é o mesmo que a resposta de Jim Schmehil.

Mihkel Müür
fonte
3
Isso não funcionou para mim, o usuário conectado conseguiu ignorar o atributo, mesmo que o usuário não tivesse nenhuma das funções.
precisa saber é o seguinte
9
Esta resposta é melhor para quando você estiver usando constantes como seus valores: ou seja, [Roles (Constants.Admin, Constants.Owner)]
dalcam
3
esta é a melhor resposta
IgorShch
18

Para o MVC4, usando a Enum( UserRoles) com minhas funções, eu uso um costume AuthorizeAttribute.

Na minha ação controlada, faço:

[CustomAuthorize(UserRoles.Admin, UserRoles.User)]
public ActionResult ChangePassword()
{
    return View();
}

E eu uso um costume AuthorizeAttributeassim:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CustomAuthorize : AuthorizeAttribute
{
    private string[] UserProfilesRequired { get; set; }

    public CustomAuthorize(params object[] userProfilesRequired)
    {
        if (userProfilesRequired.Any(p => p.GetType().BaseType != typeof(Enum)))
            throw new ArgumentException("userProfilesRequired");

        this.UserProfilesRequired = userProfilesRequired.Select(p => Enum.GetName(p.GetType(), p)).ToArray();
    }

    public override void OnAuthorization(AuthorizationContext context)
    {
        bool authorized = false;

        foreach (var role in this.UserProfilesRequired)
            if (HttpContext.Current.User.IsInRole(role))
            {
                authorized = true;
                break;
            }

        if (!authorized)
        {
            var url = new UrlHelper(context.RequestContext);
            var logonUrl = url.Action("Http", "Error", new { Id = 401, Area = "" });
            context.Result = new RedirectResult(logonUrl);

            return;
        }
    }
}

Isso faz parte do FNHMVC modificado de Fabricio Martínez Tamayo https://github.com/fabriciomrtnz/FNHMVC/

Bernardo Loureiro
fonte
1
Seu método OnAuthorization exigirá que o usuário tenha todas as funções enumeradas; Isso foi intencional ou você está perdendo uma pausa nesse ciclo?
Tieson T.
@ Tieson: Eu inspecionei isso bem de perto, definitivamente parece que uma pausa seria necessária nesse loop.
OcelotXL
@TiesonT. e @ madrush, agradeço a sua correção, ele realmente pode ter uma pausa dentro do loop. Vou mudar o código acima.
Bernardo Loureiro
A enumeração UserRoles é agradável. Você o declara manualmente ou é gerado automaticamente com base no conteúdo do banco de dados?
Konrad Viltersten
@KonradViltersten É manualmente, mas eu acho que com reflexão e classe dinâmica gerada automaticamente pode ser feito
Bernardo Loureiro
3

Outra solução clara, você pode usar constantes para manter a convenção e adicionar vários atributos [Autorizar]. Veja isso:

public static class RolesConvention
{
    public const string Administrator = "Administrator";
    public const string Guest = "Guest";
}

Então no controlador:

[Authorize(Roles = RolesConvention.Administrator )]
[Authorize(Roles = RolesConvention.Guest)]
[Produces("application/json")]
[Route("api/[controller]")]
public class MyController : Controller
Renê R. Silva
fonte
14
Vários Authorizeatributos empregam semântica AND e exigem que TODAS as condições sejam atendidas (ou seja, o usuário deve estar nas funções de Administrador e Convidado).
trousyt
3

Se você aplicar essas duas funções com frequência, poderá envolvê-las em sua própria Autorização. Esta é realmente uma extensão da resposta aceita.

using System.Web.Mvc;

public class AuthorizeAdminOrMember : AuthorizeAttribute
{
    public AuthorizeAdminOrMember()
    {
        Roles = "members, admin";
    }
}

E, em seguida, aplique sua nova autorização à Ação. Eu acho que isso parece mais limpo e lê facilmente.

public class MyController : Controller
{
    [AuthorizeAdminOrMember]
    public ActionResult MyAction()
    {
        return null;
    }
}
GER
fonte
1

Melhor código com a adição de uma subclasse AuthorizeRole.cs

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
    class AuthorizeRoleAttribute : AuthorizeAttribute
    {
        public AuthorizeRoleAttribute(params Rolenames[] roles)
        {
            this.Roles = string.Join(",", roles.Select(r => Enum.GetName(r.GetType(), r)));
        }
        protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.Request.IsAuthenticated)
            {
                filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                  { "action", "Unauthorized" },
                  { "controller", "Home" },
                  { "area", "" }
                  }
              );
                //base.HandleUnauthorizedRequest(filterContext);
            }
            else
            {
                filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary {
                  { "action", "Login" },
                  { "controller", "Account" },
                  { "area", "" },
                  { "returnUrl", HttpContext.Current.Request.Url }
                  }
              );
            }
        }
    }

Como usar isso

[AuthorizeRole(Rolenames.Admin,Rolenames.Member)]

public ActionResult Index()
{
return View();
}
kinzzy goel
fonte
1

Usando o AspNetCore 2.x, você precisa seguir um caminho um pouco diferente:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AuthorizeRoleAttribute : AuthorizeAttribute
{
    public AuthorizeRoleAttribute(params YourEnum[] roles)
    {
        Policy = string.Join(",", roles.Select(r => r.GetDescription()));
    }
}

basta usá-lo assim:

[Authorize(YourEnum.Role1, YourEnum.Role2)]
Daniel DirtyNative Martin
fonte
-2
Intent promptInstall = new Intent(android.content.Intent.ACTION_VIEW);
promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
promptInstall.setDataAndType(Uri.parse("http://10.0.2.2:8081/MyAPPStore/apk/Teflouki.apk"), "application/vnd.android.package-archive" );

startActivity(promptInstall);
Orsit Moel
fonte
1
As respostas, incluindo o código, devem ter pelo menos uma descrição mínima, explicando como o código funciona e por que ele responde à pergunta. Além disso, a formatação da seção de código precisa ser aprimorada.
Roberto Caboni 03/01
Hã? @Orsit Moel, parece copiar-colado no segmento errado ...
Cameron Forward