Otorisasi Atribut dengan Banyak Peran

97

Saya ingin menambahkan Otorisasi ke pengontrol, untuk beberapa Peran sekaligus.

Biasanya akan terlihat seperti ini:

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

Tapi saya telah menyimpan Peran saya dalam konstanta, karena mereka mungkin berubah atau diperpanjang di beberapa titik.

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

Saya tidak dapat melakukan ini, karena string harus diketahui pada waktu kompilasi:

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

Apakah ada cara untuk menghindari masalah tersebut?

Saya BISA menulis const yang hanya berisi "RoleA, RoleB, RoleC" - tetapi saya tidak suka string ajaib dan ini adalah string ajaib. Mengubah nama Role dan lupa mengganti string gabungan akan menjadi bencana.

Saya menggunakan MVC5. Identitas ASP.NET dan Peran diketahui pada waktu kompilasi.

Christian Sauer
sumber
apakah Anda menggunakan string konstanta publik RoleA = "RoleA"; atau seperti yang Anda tulis dalam pertanyaan?
Mukesh Modhvadiya

Jawaban:

188

Coba buat atribut otorisasi khusus seperti ini .

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

Dengan asumsi peran Anda akan sama untuk beberapa pengontrol, buat kelas helper:

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

Kemudian gunakan seperti ini:

public class MyController : Controller
{
    [AuthorizeRoles(Role.Administrator, Role.Assistant)]
    public ActionResult AdminOrAssistant()
    {                       
        return View();
    }
}
MacGyver
sumber
12
Nah, itu ide yang layak untuk Mac Gyver;)
Christian Sauer
2
Solusi yang sangat bagus :)
aup
1
Saya juga sangat menyukai solusi ini, terutama karena saya bisa membiarkan Peran saya menjadi enum daripada string. Apa namespace dan lokasi yang baik dalam hierarki proyek untuk menempatkan atribut otorisasi khusus ini?
Simon Shine
4
Saya tidak yakin apa yang terjadi di sini, tetapi ini TIDAK membantu saya, pengguna mana pun terlepas dari perannya dapat mengakses metode ini.
Urielzen
2
Masalah yang sama seperti @Urielzen, tetapi telah diperbaiki oleh jawaban di bawah ini dari Jerry Finegan (menggunakan "System.Web.Mvc.AuthorizeAttribute and NOT System.Web.Http.AuthorizeAttribute")
RJB
13

Pastikan Anda menonaktifkan kelas atribut khusus System.Web.Mvc.AuthorizeAttributedan BUKAN System.Web.Http.AuthorizeAttribute.

Saya mengalami masalah yang sama. Setelah saya mengubahnya, semuanya bekerja.

Anda mungkin juga ingin menambahkan yang berikut ini ke kelas atribut khusus Anda:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 
Jerry Finegan
sumber
Saya baru saja mencoba ini dan menemukan referensi perpustakaan System.Web.Http.AuthorizeAttributeBUKAN DARISystem.Web.Mvc.AuthorizeAttribute
fraser jordan
11

Cara terbaik dan termudah yang saya temukan untuk menyelesaikan masalah ini adalah dengan menggabungkan peran di atribut Otorisasi.

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

dengan CustomRole kelas dengan string konstan seperti ini:

public static class CustomRoles
{
    public const string Admin = "Admin";
    // and so on..
}
ChristopheHvd
sumber
2
Berharga; tapi ini harus menjadi komentar; bukan jawaban.
GhostCat
1
Solusi sederhana dan elegan!
Iosif Bancioiu
Jawaban Anda dan jawaban yang diterima akan memicu otorisasi jika diterapkan dengan benar (saya menggunakan yang diterima di aplikasi web produksi). Mengusulkan pengeditan untuk menghapus komentar tentang jawaban yang diterima.
Eric Eskildsen
3

Apa yang saya lakukan adalah jawabannya di @Tieson

Saya mengubah sedikit dalam jawabannya. Alih-alih string. Bergabunglah mengapa tidak mengubahnya menjadi daftar?

Inilah jawaban saya:

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

Lalu periksa apakah peran tersebut valid menggantikan 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);
            }
        }

Dan begitulah, sekarang memvalidasi apakah peran tersebut berwenang untuk mengakses sumber daya

Christopher Enriquez
sumber
1

Saya merasa bahwa atribut otorisasi khusus berlebihan untuk masalah ini kecuali Anda memiliki banyak peran.

Karena string harus diketahui pada waktu kompilasi, mengapa tidak membuat kelas Peran statis yang berisi string publik dari peran yang telah Anda tetapkan, lalu tambahkan string yang dipisahkan koma dengan peran tertentu yang ingin Anda otorisasi:

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

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

Dan kemudian Anda dapat menggunakan Atribut Otorisasi seperti pada Kelas Pengontrol atau Metode Pengontrol (atau keduanya):

[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
{
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER)
    public ActionResult Create()
    {
        ..code here...
    }
}
Robert Tuttle
sumber
1
Contoh ini tidak berhasil, atau setidaknya tidak seperti yang Anda pikirkan. Misalnya, ketika novel, ADMIN_OR_VIEWERperan dalam tindakan itu mubazir karena Anda tidak akan diizinkan untuk masuk ke Createmetode jika Anda belum memiliki ADMINperan tersebut. Dalam hal ini VIEWERtidak akan pernah bisa memanggil Createmetode.
John Leidegren
Solusi ini juga tidak dapat diskalakan. Akan ada titik di mana Anda memiliki terlalu banyak peran dengan tindakan berbeda dan Anda tidak boleh membuat setiap kombinasi
EduLopez