อนุญาตให้หลายบทบาทเข้าถึงแอ็คชันคอนโทรลเลอร์


274

ตอนนี้ฉันตกแต่งวิธีเช่นนี้เพื่ออนุญาตให้ "สมาชิก" เข้าถึงการกระทำของตัวควบคุมของฉัน

[Authorize(Roles="members")]

ฉันจะอนุญาตให้มีมากกว่าหนึ่งบทบาทได้อย่างไร ตัวอย่างต่อไปนี้ใช้ไม่ได้ แต่แสดงให้เห็นว่าฉันพยายามทำอะไร (อนุญาตให้เข้าถึง "สมาชิก" และ "ผู้ดูแลระบบ"):

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

4
โปรดเปลี่ยนคำตอบที่ยอมรับสำหรับคำถามนี้ บุคคลที่มีคำตอบที่ได้รับการยอมรับแก้ไขได้ระบุว่าเขาผิด
Eric J.

คำตอบ:


595

ตัวเลือกอื่นคือการใช้ตัวกรองอนุญาตแบบเดียวขณะที่คุณโพสต์ แต่ลบการเสนอราคาภายใน

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

5
ทำงานใน MVC 5 ด้วย +1
gkonuralp

4
ใช้งานได้ใน ASP.NET Core 1.0 (MVC 6) และ Microsoft.AspNet.Identity v3. *
Soren

3
ไม่เป็นไรถ้าคุณมีคอนโทรลเลอร์เพียงตัวเดียวที่คุณต้องอนุญาต หากคุณมีมากกว่าหนึ่งคุณกำลังทำซ้ำค่าคงที่สตริงเหล่านั้น (yuck) ฉันชอบคลาสสแตติกที่มีชื่อบทบาทมาก ความเกลียดชังสัตว์เลี้ยงของฉันเป็นสตริงที่ซ้ำกัน ... แย่มาก
robnick

1
@ Kraeg ข่าวดีที่คุณแก้ไขปัญหาของคุณ ตอนนี้ให้พิจารณาลบความคิดเห็นของคุณโปรด
Pablo Claus

1
ทำไม? ฉันใช้เวลานานพอสมควรในการทำสิ่งนี้ อาจเป็นประโยชน์สำหรับคนอื่นที่ประสบปัญหาเดียวกัน
kraeg

129

หากคุณต้องการใช้บทบาทที่กำหนดเองคุณสามารถทำได้:

CustomRoles ระดับ:

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

การใช้

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

หากคุณมีบทบาทน้อยคุณอาจรวมเข้าด้วยกัน (เพื่อความชัดเจน) เช่นนี้:

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

การใช้

[Authorize(Roles = CustomRoles.AdministratorOrUser)]

7
นี่จะเป็นคำตอบที่ดีถ้าคุณอธิบายให้คนที่ไม่รู้ว่าอยู่เบื้องหลัง CustomRoles อย่างไร
James Skemp

1
@JamesSkemp ตกลงฉันได้ขยายคำตอบของฉัน มันง่ายมาก CustumRoles เป็นคลาสที่ฉันสร้างขึ้นซึ่งมีค่าคงที่บางอย่างที่สอดคล้องกับบทบาทแอปพลิเคชันของฉัน ฉันทำเช่นนั้นด้วยเหตุผลสองสามข้อ: 1) อนุญาตให้ใช้ระบบ Intellisense เพื่อหลีกเลี่ยงการสะกดผิด 2) เพื่อให้การบำรุงรักษาง่ายขึ้น หากมีการเปลี่ยนแปลงบทบาทฉันต้องอัปเดตสถานที่เดียวภายในแอปพลิเคชันของฉัน
Pablo Claus

@Pabloker อีกทางหนึ่งคุณสามารถสร้าง enum ด้วยแอตทริบิวต์ Flags เช่น Convert.ToString (CustomRoles.Administrator | CustomRoles.User); - ส่วนที่น่ารำคาญก็คือสิ่งนี้ต้องการการแปลงที่ชัดเจน
cstruter

หากคุณมี 39 บทบาท?
Kiquenet

ผมคิดว่าปัญหาของคุณจะต้องผ่านการสร้างแบบจำลองของใบอนุญาตเกินกว่าสิ่งที่สามารถทำได้ด้วยสุทธิ
ปาโบลซานตาคลอส

82

การทำให้เข้าใจง่ายอย่างหนึ่งที่เป็นไปได้คือ subclass AuthorizeAttribute:

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

การใช้งาน:

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

ความหมายเหมือนกับคำตอบของ Jim Schmehil


3
สิ่งนี้ไม่ได้ผลสำหรับฉันผู้ใช้ที่ล็อกอินสามารถข้ามแอททริบิวต์ได้แม้ว่าผู้ใช้จะไม่มีบทบาทใด ๆ
Urielzen

9
คำตอบนี้ดีกว่าเมื่อคุณใช้ค่าคงที่เป็นค่าของคุณ: เช่น [บทบาท (ค่าคงที่, ค่าคงที่, ค่าคงที่))
dalcam

3
นี่คือคำตอบที่ดีที่สุด
IgorShch

18

สำหรับ MVC4 ใช้Enum( UserRoles) AuthorizeAttributeกับบทบาทของฉันฉันใช้กำหนดเอง

ในการควบคุมของฉันฉันจะ:

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

และฉันใช้กำหนดเองAuthorizeAttributeเช่นนั้น:

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

นี่เป็นส่วนหนึ่งของ FNHMVC ที่ปรับเปลี่ยนโดย Fabricio Martínez Tamayo https://github.com/fabriciomrtnz/FNHMVC/


1
วิธีการ OnAuthorization ของคุณจะกำหนดให้ผู้ใช้ต้องมีบทบาทที่แจกแจงทั้งหมด เป็นความตั้งใจนั้นหรือคุณพลาดการพักในวงนั้น
Tieson T.

@Tieson: ฉันตรวจสอบว่าสวยอย่างใกล้ชิดดูเหมือนว่าแน่นอนจะต้องหยุดพักในวงนั้น
OcelotXL

@TiesonT และ @ madrush ฉันขอขอบคุณการแก้ไขของคุณมันอาจมีตัวแบ่งในวง ฉันจะเปลี่ยนรหัสด้านบน
Bernardo Loureiro

UserRoles enum นั้นดี คุณประกาศด้วยตนเองหรือสร้างโดยอัตโนมัติตามเนื้อหาของฐานข้อมูลหรือไม่?
Konrad Viltersten

@KonradViltersten มันเป็นแบบแมนนวล แต่ฉันเดาได้ว่า Reflection และ Dynamic class autogenerated สามารถทำได้
Bernardo Loureiro

3

โซลูชันที่ชัดเจนอีกวิธีหนึ่งคุณสามารถใช้ค่าคงที่เพื่อรักษาการประชุมและเพิ่มแอตทริบิวต์ [อนุญาต] หลายรายการ ลองดู:

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

จากนั้นในตัวควบคุม:

[Authorize(Roles = RolesConvention.Administrator )]
[Authorize(Roles = RolesConvention.Guest)]
[Produces("application/json")]
[Route("api/[controller]")]
public class MyController : Controller

14
Authorizeแอตทริบิวต์หลายรายการใช้งานและซีแมนทิกส์และต้องการให้ตรงตามเงื่อนไขทั้งหมด (เช่นผู้ใช้จะต้องอยู่ในบทบาทผู้ดูแลระบบและผู้เยี่ยมชม)
trousyt

3

หากคุณพบว่าตัวเองใช้บทบาททั้งสองนี้บ่อยครั้งคุณสามารถสรุปได้ในสิทธิ์ของตัวเอง นี่เป็นส่วนขยายของคำตอบที่ยอมรับได้จริง ๆ

using System.Web.Mvc;

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

จากนั้นใช้การอนุญาตใหม่กับการดำเนินการ ฉันคิดว่ามันดูสะอาดและอ่านได้ง่าย

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

1

โค้ดที่ดีขึ้นด้วยการเพิ่มคลาสย่อย 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 }
                  }
              );
            }
        }
    }

วิธีใช้งาน

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

public ActionResult Index()
{
return View();
}

1

การใช้ AspNetCore 2.x คุณต้องใช้วิธีที่แตกต่าง:

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

เพียงใช้มันเช่นนี้

[Authorize(YourEnum.Role1, YourEnum.Role2)]

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

1
คำตอบรวมถึงรหัสควรมีคำอธิบายขั้นต่ำอย่างน้อยอธิบายวิธีการทำงานของรหัสและสาเหตุที่คำตอบของคำถาม นอกจากนี้ยังจำเป็นต้องปรับปรุงการจัดรูปแบบส่วนของโค้ด
Roberto Caboni

ฮะ? @Orsit Moel ดูเหมือนว่าสำเนาถูกวางลงในเธรดที่ไม่ถูกต้อง ...
Cameron Forward
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.