ASP.NET MVC 4 niestandardowy atrybut autoryzacji z kodami uprawnień (bez ról)

121

Muszę kontrolować dostęp do widoków na podstawie poziomów uprawnień użytkowników (nie ma ról, tylko poziomy uprawnień dla poziomów operacji CRUD przypisanych do użytkowników) w mojej aplikacji MVC 4.

Jako przykład; poniżej AuthorizeUser będzie mój atrybut niestandardowy i muszę go używać w następujący sposób:

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

Czy można to zrobić w ten sposób?

chatura
źródło

Odpowiedzi:

244

Mogę to zrobić za pomocą atrybutu niestandardowego w następujący sposób.

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

Custom Attribute w następujący sposób.

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

Możesz przekierować nieautoryzowanego użytkownika w swoim niestandardowym AuthorisationAttribute, zastępując HandleUnauthorizedRequestmetodę:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(
                    new
                        { 
                            controller = "Error", 
                            action = "Unauthorised" 
                        })
                );
}
chatura
źródło
Wypróbowałem Twój przykład HandleUnauthorizedRequest, ale kiedy określam RouteValueDictionary, po prostu przekierowuje do mnie trasę, która nie istnieje. Dodaje trasę, na którą chcę przekierować użytkownika, do trasy, do której użytkownik chciał uzyskać dostęp ... si Otrzymuję coś takiego: localhost: 9999 / admin / Home, kiedy chciałem localhost: 9999 / Home
Marin
1
@Marin Spróbuj dodać area = string.Empty w RouteValueDictionary
Alex
30
Głosowałem za głosem, ale potem zobaczyłem „if (warunek) {return true;} else {return false;}” na końcu ....
GabrielBB
1
@Emil Po prostu zwróciłbym wartość logiczną, którą dała mi metoda String.Contains. Ale to nie ma znaczenia, nie głosowałem przeciw, po prostu nie głosowałem za hehe.
GabrielBB
2
.Name.ToString()jest zbędne, ponieważ Namewłaściwość jest już ciągiem znaków
FindOutIslamNow
13

Oto modyfikacja dla pliku prev. odpowiedź. Główna różnica polega na tym, że gdy użytkownik nie jest uwierzytelniony, używa oryginalnej metody „HandleUnauthorizedRequest” do przekierowania na stronę logowania:

   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
źródło
3

Może jest to przydatne dla każdego w przyszłości, zaimplementowałem niestandardowy atrybut autoryzacji, taki jak ten:

[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
źródło
0

Jeśli używasz WEB API z Claims, możesz użyć tego:

[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
źródło