ให้สิทธิ์แอตทริบิวต์ที่มีหลายบทบาท


100

ฉันต้องการเพิ่มการอนุญาตให้กับคอนโทรลเลอร์สำหรับหลาย ๆ บทบาทพร้อมกัน

โดยปกติจะมีลักษณะดังนี้:

[Authorize(Roles = "RoleA,RoleB,RoleC")]
public async Task<ActionResult> Index()
{
}

แต่ฉันได้จัดเก็บบทบาทของฉันไว้ใน consts เนื่องจากอาจมีการเปลี่ยนแปลงหรือถูกขยายออกไป

public const RoleA = "RoleA";
public const RoleB = "RoleB";
public const RoleC = "RoleC";

ฉันไม่สามารถทำได้เนื่องจากต้องทราบสตริงในเวลาคอมไพล์:

[Authorize(Roles = string.join(",",RoleA,RoleB,RoleC)]
public async Task<ActionResult> Index()
{
}

มีวิธีหลีกเลี่ยงปัญหาหรือไม่?

ฉันสามารถเขียน const ซึ่งมีเพียง "RoleA, RoleB, RoleC" - แต่ฉันไม่ชอบสายเวทย์มนตร์และนี่คือสตริงเวทย์มนตร์ การเปลี่ยนชื่อบทบาทและลืมเปลี่ยนสตริงที่รวมกันอาจเป็นหายนะ

ฉันใช้ MVC5 ASP.NET Identity และ Role เป็นที่รู้จักในเวลาคอมไพล์


คุณใช้สตริง const สาธารณะ RoleA = "RoleA"; หรือตามที่คุณเขียนไว้ในคำถาม?
Mukesh Modhvadiya

คำตอบ:


199

พยายามที่จะสร้างแอตทริบิวต์อนุมัติกำหนดเองเช่นนี้

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

สมมติว่าบทบาทของคุณจะเหมือนกันสำหรับคอนโทรลเลอร์หลายตัวให้สร้างคลาสตัวช่วย:

public static class Role
{
    public const string Administrator = "Administrator";
    public const string Assistant = "Assistant";
}

จากนั้นใช้มันดังนี้:

public class MyController : Controller
{
    [AuthorizeRoles(Role.Administrator, Role.Assistant)]
    public ActionResult AdminOrAssistant()
    {                       
        return View();
    }
}

13
ตอนนี้เป็นความคิดที่คู่ควรกับ Mac Gyver;)
Christian Sauer

2
ทางออกที่ดีมาก :)
aup

1
ฉันชอบโซลูชันนี้มากโดยเฉพาะอย่างยิ่งเพราะฉันสามารถปล่อยให้บทบาทของฉันเป็น enum แทนที่จะเป็นสตริง เนมสเปซและตำแหน่งที่ดีในลำดับชั้นของโปรเจ็กต์จะเป็นอย่างไรสำหรับการวางแอตทริบิวต์การอนุญาตที่กำหนดเองนี้
Simon Shine

4
ฉันไม่แน่ใจว่าเกิดอะไรขึ้นที่นี่ แต่สิ่งนี้ไม่ได้ช่วยฉันผู้ใช้ไม่ว่าจะอยู่ในบทบาทใดก็สามารถเข้าถึงวิธีการได้
Urielzen

2
ปัญหาเดียวกับ @Urielzen แต่มันก็แก้ไขโดยคำตอบด้านล่างจากเจอร์รี่ Finegan (โดยใช้ "System.Web.Mvc.AuthorizeAttribute และไม่ System.Web.Http.AuthorizeAttribute")
RJB

13

ตรวจสอบให้แน่ใจว่าคุณกำลัง deriving ระดับแอตทริบิวต์ที่กำหนดเองของคุณปิดและไม่System.Web.Mvc.AuthorizeAttributeSystem.Web.Http.AuthorizeAttribute

ฉันพบปัญหาเดียวกัน เมื่อฉันเปลี่ยนมันทุกอย่างก็ใช้ได้

คุณอาจต้องการเพิ่มสิ่งต่อไปนี้ในคลาสแอตทริบิวต์ที่กำหนดเองของคุณ:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 

ฉันเพิ่งลองสิ่งนี้และพบว่ามีการอ้างอิงถึงห้องสมุดSystem.Web.Http.AuthorizeAttributeแทนSystem.Web.Mvc.AuthorizeAttribute
จอร์แดน

13

วิธีที่ดีที่สุดและง่ายที่สุดที่ฉันพบในการแก้ไขปัญหานี้คือการเชื่อมต่อบทบาทในแอตทริบิวต์ Authorize

[Authorize(Roles = CustomRoles.Admin + "," + CustomRoles.OtherRole)]

ด้วย CustomRole คลาสที่มีสตริงคงที่เช่นนี้:

public static class CustomRoles
{
    public const string Admin = "Admin";
    // and so on..
}

2
คุ้มค่า; แต่นี่ควรเป็นความคิดเห็น ไม่ใช่คำตอบ
GhostCat

1
วิธีง่ายๆและสวยหรู!
Iosif Bancioiu

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

ถ้า Roles เป็น Enums คุณสามารถใช้บางอย่างเช่น [Authorize (Roles = nameof (UserRoleEnum.User) + "," + nameof (UserRoleEnum.Admin))]
Varun

3

สิ่งที่ฉันทำคือคำตอบใน @Tieson

ฉันปรับคำตอบของเขาเล็กน้อย แทนที่จะเป็นสตริงเข้าร่วมทำไมไม่แปลงเป็นรายการ?

นี่คือคำตอบของฉัน:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    private new List<string> Roles;
    public AuthorizeRolesAttribute(params string[] roles) : base()
    {
        Roles = roles.toList()
    }
}

จากนั้นตรวจสอบว่าบทบาทนั้นถูกต้องในการลบล้าง OnAuthorization หรือไม่

public override void OnAuthorization(HttpActionContext actionContext)
{
            if (Roles == null)
                HandleUnauthorizedRequest(actionContext);
            else
            {
                ClaimsIdentity claimsIdentity = HttpContext.Current.User.Identity as ClaimsIdentity;
                string _role = claimsIdentity.FindFirst(ClaimTypes.Role).Value;
                bool isAuthorize = Roles.Any(role => role == _role);

                if(!isAuthorize)
                    HandleUnauthorizedRequest(actionContext);
            }
        }

และคุณมีแล้วขณะนี้กำลังตรวจสอบว่าบทบาทได้รับอนุญาตให้เข้าถึงทรัพยากรหรือไม่


1

ฉันรู้สึกว่าแอตทริบิวต์การอนุญาตที่กำหนดเองนั้นเกินความจำเป็นสำหรับปัญหานี้เว้นแต่คุณจะมีบทบาทจำนวนมาก

เนื่องจากสตริงต้องเป็นที่รู้จักในเวลาคอมไพล์ทำไมไม่สร้างคลาส Role แบบคงที่ที่มีสตริงสาธารณะของบทบาทที่คุณกำหนดไว้จากนั้นเพิ่มสตริงที่คั่นด้วยเครื่องหมายจุลภาคด้วยบทบาทบางอย่างที่คุณต้องการอนุญาต:

public static class Roles
{
    public const string ADMIN = "Admin";
    public const string VIEWER = "Viewer";

    public const string ADMIN_OR_VIEWER = ADMIN + "," + VIEWER;
}

จากนั้นคุณสามารถใช้ Authorize Attribute เช่นนี้ใน Controller Class หรือ Controller Method (หรือทั้งสองอย่าง):

[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
{
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER)
    public ActionResult Create()
    {
        ..code here...
    }
}

1
ตัวอย่างนี้ใช้ไม่ได้ผลหรืออย่างน้อยก็ไม่ใช่อย่างที่คุณคิด ตัวอย่างเช่นในขณะที่นวนิยายADMIN_OR_VIEWERบทบาทในการดำเนินการซ้ำซ้อนเนื่องจากคุณจะไม่ได้รับอนุญาตให้เข้าสู่Createเมธอดหากคุณยังไม่มีADMINบทบาท ในกรณีนี้VIEWERจะไม่สามารถเรียกใช้Createเมธอดได้
John Leidegren

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