Atributo de autorização personalizado do ASP.NET MVC 4 com códigos de permissão (sem funções)

121

Preciso controlar o acesso às visualizações com base nos níveis de privilégio dos usuários (não há funções, apenas níveis de privilégio para os níveis de operação CRUD atribuídos aos usuários) no meu aplicativo MVC 4.

Como um exemplo; abaixo do AuthorizeUser será meu atributo personalizado e preciso usá-lo assim:

[AuthorizeUser(AccessLevels="Read Invoice, Update Invoice")]
public ActionResult UpdateInvoice(int invoiceId)
{
   // some code...
   return View();
}


[AuthorizeUser(AccessLevels="Create Invoice")]
public ActionResult CreateNewInvoice()
{
  // some code...
  return View();
}


[AuthorizeUser(AccessLevels="Delete Invoice")]
public ActionResult DeleteInvoice(int invoiceId)
{
  // some code...
  return View();
}

É possível fazer dessa maneira?

chatura
fonte

Respostas:

243

Eu poderia fazer isso com um atributo personalizado da seguinte maneira.

[AuthorizeUser(AccessLevel = "Create")]
public ActionResult CreateNewInvoice()
{
    //...
    return View();
}

Classe de atributo personalizado da seguinte maneira.

public class AuthorizeUserAttribute : AuthorizeAttribute
{
    // Custom property
    public string AccessLevel { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var isAuthorized = base.AuthorizeCore(httpContext);
        if (!isAuthorized)
        {                
            return false;
        }

        string privilegeLevels = string.Join("", GetUserRights(httpContext.User.Identity.Name.ToString())); // Call another method to get rights of the user from DB

        return privilegeLevels.Contains(this.AccessLevel);           
    }
}

Você pode redirecionar um usuário não autorizado em seu costume AuthorisationAttribute, substituindo o HandleUnauthorizedRequestmétodo:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(
                    new
                        { 
                            controller = "Error", 
                            action = "Unauthorised" 
                        })
                );
}
chatura
fonte
Eu tentei o seu exemplo de HandleUnauthorizedRequest, mas quando eu especifico o RouteValueDictionary, ele apenas redireciona para mim uma rota que não existe. Ele anexa a rota que eu quero redirecionar o usuário para a rota que o usuário queria acessar ... si recebo algo como: localhost: 9999 / admin / Home quando eu queria localhost: 9999 / Home
Marin
1
@Marin Tente adicionar area = string.Empty no RouteValueDictionary
Alex
30
Eu estava votando positivamente, mas então vi "if (condition) {return true;} else {return false;}" no final ....
GabrielBB 20/16/16
1
@ Emil Simplesmente retornaria o booleano que o método String.Contains me deu. Mas isso é irrelevante, eu não diminuí o voto, apenas não votei hehe.
GabrielBB 17/10
2
.Name.ToString()é redundante, já que a Namepropriedade já é string
FindOutIslamNow 29/04
13

Aqui está uma modificação para o anterior. responda. A principal diferença é que, quando o usuário não é autenticado, ele usa o método "HandleUnauthorizedRequest" original para redirecionar para a página de login:

   protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {

        if (filterContext.HttpContext.User.Identity.IsAuthenticated) {

            filterContext.Result = new RedirectToRouteResult(
                        new RouteValueDictionary(
                            new
                            {
                                controller = "Account",
                                action = "Unauthorised"
                            })
                        );
        }
        else
        {
             base.HandleUnauthorizedRequest(filterContext);
        }
    }
Leonid Minkov
fonte
3

Talvez isso seja útil para qualquer pessoa no futuro, eu implementei um Atributo de Autorização personalizado como este:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ClaimAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    private readonly string _claim;

    public ClaimAuthorizeAttribute(string Claim)
    {
        _claim = Claim;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;
        if(user.Identity.IsAuthenticated && user.HasClaim(ClaimTypes.Name, _claim))
        {
            return;
        }

        context.Result = new ForbidResult();
    }
}
RaphaelH
fonte
0

Se você usa a API WEB com declarações, pode usar o seguinte:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AutorizeCompanyAttribute:  AuthorizationFilterAttribute
{
    public string Company { get; set; }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var claims = ((ClaimsIdentity)Thread.CurrentPrincipal.Identity);
        var claim = claims.Claims.Where(x => x.Type == "Company").FirstOrDefault();

        string privilegeLevels = string.Join("", claim.Value);        

        if (privilegeLevels.Contains(this.Company)==false)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Usuario de Empresa No Autorizado");
        }
    }
}
[HttpGet]
[AutorizeCompany(Company = "MyCompany")]
[Authorize(Roles ="SuperAdmin")]
public IEnumerable MyAction()
{....
}
Maurico Bello
fonte