Attribut d'autorisation personnalisé ASP.NET MVC 4 avec codes d'autorisation (sans rôles)

121

Je dois contrôler l'accès aux vues en fonction des niveaux de privilège des utilisateurs (il n'y a pas de rôles, uniquement des niveaux de privilège pour les niveaux de fonctionnement CRUD attribués aux utilisateurs) dans mon application MVC 4.

Par exemple; sous AuthorizeUser sera mon attribut personnalisé et je dois l'utiliser comme ceci:

[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();
}

Est-il possible de le faire de cette façon?

chatura
la source

Réponses:

243

Je pourrais le faire avec un attribut personnalisé comme suit.

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

Classe d'attribut personnalisé comme suit.

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);           
    }
}

Vous pouvez rediriger un utilisateur non autorisé dans votre personnalisation AuthorisationAttributeen remplaçant la HandleUnauthorizedRequestméthode:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(
                    new
                        { 
                            controller = "Error", 
                            action = "Unauthorised" 
                        })
                );
}
chatura
la source
J'ai essayé votre exemple de HandleUnauthorizedRequest mais lorsque je spécifie le RouteValueDictionary, il me redirige simplement une route qui n'existe pas. Il ajoute la route vers laquelle je veux rediriger l'utilisateur vers la route à laquelle l'utilisateur voulait accéder ... si j'obtiens quelque chose comme: localhost: 9999 / admin / Home quand je voulais localhost: 9999 / Home
Marin
1
@Marin Essayez d'ajouter area = string.Empty dans le RouteValueDictionary
Alex
30
J'étais en train de voter mais j'ai vu "if (condition) {return true;} else {return false;}" à la fin ....
GabrielBB
1
@Emil Je retournerais simplement le booléen que la méthode String.Contains m'a donné. Mais ce n'est pas pertinent, je n'ai pas voté contre, je n'ai tout simplement pas voté pour hehe.
GabrielBB
2
.Name.ToString()est redondant, car la Namepropriété est déjà une chaîne
FindOutIslamNow
13

Voici une modification pour le précédent. répondre. La principale différence est que lorsque l'utilisateur n'est pas authentifié, il utilise la méthode originale "HandleUnauthorizedRequest" pour rediriger vers la page de connexion:

   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);
        }
    }
Léonid Minkov
la source
3

Peut-être que cela est utile à n'importe qui à l'avenir, j'ai implémenté un attribut d'autorisation personnalisé comme celui-ci:

[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
la source
0

Si vous utilisez l'API WEB avec des revendications, vous pouvez utiliser ceci:

[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
la source