2010-05-13 33 views
25

ASP.NET MVC có hỗ trợ tốt cho bảo mật dựa trên vai trò, nhưng việc sử dụng các chuỗi như tên vai trò là maddening, đơn giản bởi vì chúng không thể được đánh máy mạnh mẽ như liệt kê.Tên chuỗi không phải là chuỗi trong ASP.NET MVC?

Ví dụ: tôi có vai trò "Quản trị" trong ứng dụng của mình. Chuỗi "Admin" bây giờ tồn tại trong thuộc tính Authorize của hành động của tôi, trong trang chủ của tôi (để ẩn tab), trong cơ sở dữ liệu của tôi (để xác định vai trò có sẵn cho mỗi người dùng) và bất kỳ vị trí nào khác trong mã hoặc chế độ xem của tôi các tệp mà tôi cần thực hiện logic đặc biệt cho người dùng quản trị hoặc không phải quản trị viên.

Có giải pháp nào tốt hơn, viết ngắn thuộc tính ủy quyền và bộ lọc của riêng tôi, có lẽ sẽ xử lý một tập hợp các giá trị điều tra?

Trả lời

19

Tôi thường sử dụng một lớp với một chuỗi các hằng số chuỗi. Nó không phải là một giải pháp hoàn hảo, vì bạn cần phải nhớ để sử dụng nó ở khắp mọi nơi, nhưng ít nhất nó được thoát khỏi khả năng lỗi chính tả.

static class Role { 
    public const string Admin = "Admin"; 
} 
+0

Tôi đã đi với giải pháp này do nó đơn giản. Những thay đổi mã là tối thiểu, vì tôi chỉ phải thay thế các chuỗi mã hóa cứng với các tham chiếu liên tục. – MikeWyatt

3

Nó không phải rằng cứng để tùy chỉnh AuthorizeAttribute theo cách bạn đề nghị.

Loại phụ đề, thêm thuộc tính tùy chỉnh cho loại enum của bạn và gọi ToString() trên giá trị được chuyển. Đặt điều đó vào thuộc tính vai trò thông thường. Điều này sẽ chỉ mất một vài dòng mã và AuthorizeAttribute vẫn thực hiện tất cả công việc thực tế.

+1 cho Matti, vì const cũng là một lựa chọn tốt.

2

Tôi đã sử dụng một lớp tĩnh xác định một chuỗi các hằng số chuỗi như được đề xuất bởi Matti và trên dự án hiện tại của tôi, tôi sử dụng phương thức mở rộng bên dưới với một enum. Cả hai cách tiếp cận đều hoạt động rất tốt.

public static class EnumerationExtension 
{ 
    public static string GetName(this Enum e) 
    { 
    return Enum.GetName(e.GetType(), e); 
    } 
} 
46

Sử dụng dây ma thuật mang lại cho bạn sự linh hoạt để khai báo nhiều vai trò trong các thuộc tính Authorize (ví dụ [Authorize (Roles = "Admin, Moderator")] mà bạn có xu hướng giảm khi bạn đi đến một giải pháp mạnh mẽ gõ. nhưng đây là cách bạn có thể duy trì sự linh hoạt này trong khi vẫn nhận được tất cả những gì mạnh mẽ gõ

Xác định vai trò của bạn trong một enum có sử dụng lá cờ bit:.

[Flags] 
public enum AppRole { 
    Admin = 1, 
    Moderator = 2, 
    Editor = 4, 
    Contributor = 8, 
    User = 16 
} 

Override AuthorizeAttribute:

0.123.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class MyAuthorizeAttribute : AuthorizeAttribute { 

    public AppRole AppRole { get; set; } 

    public override void OnAuthorization(AuthorizationContext filterContext) { 
     if (AppRole != 0) 
      Roles = AppRole.ToString(); 

     base.OnAuthorization(filterContext); 
    } 

} 

Bây giờ nếu bạn có thể sử dụng MyAuthorizeAttribute như thế này:

[MyAuthorize(AppRole = AppRole.Admin | AppRole.Moderator | AppRole.Editor)] 
public ActionResult Index() { 

    return View(); 
} 

Hành động trên sẽ chỉ cho phép người dùng có trong ít nhất một trong những vai trò niêm yết (Quản trị, Điều hành, hoặc Editor). Hành vi này giống với AuthorizeAttribute mặc định của MVC, ngoại trừ không có chuỗi ma thuật.

Nếu bạn sử dụng kỹ thuật này, đây là một phương pháp khuyến nông trên IPrincipal mà cũng có thể hữu ích:

public static class PrincipalExtensions { 

    public static bool IsInRole(this IPrincipal user, AppRole appRole) { 

     var roles = appRole.ToString().Split(',').Select(x => x.Trim()); 
     foreach (var role in roles) { 
      if (user.IsInRole(role)) 
       return true; 
     } 

     return false; 
    } 
} 

Bạn có thể sử dụng phương pháp mở rộng này như thế này:

public ActionResult Index() { 
    var allowed = User.IsInRole(AppRole.Admin | AppRole.Moderator | AppRole.Editor); 

    if (!allowed) { 
     // Do Something 
    } 

    return View(); 
} 
+0

Tôi thực sự thích cách tiếp cận này, thật dễ dàng để thực hiện và duy trì – Sam

+0

@Jammer, các giá trị enum không nhất thiết phải khớp với các ID cơ sở dữ liệu. Chúng có thể độc lập và vẫn hoạt động tốt. –

+0

Vâng, tôi nhận ra rằng ngay khi tôi bắt đầu sử dụng nó. Silly me ... – Jammer

10

Mặc dù nó không sử dụng enums, tôi đã sử dụng giải pháp bên dưới, nơi chúng tôi phân lớp bộ lọc ủy quyền để nhận các tham số tên vai trò có chiều dài thay đổi trong hàm tạo. Sử dụng này cùng với tên vai trò tuyên bố trong các biến const nơi nào đó, chúng ta tránh dây ma thuật:

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

public class MyController : Controller 
{ 
    private const string AdministratorRole = "Administrator"; 
    private const string AssistantRole = "Assistant"; 

    [AuthorizeRoles(AdministratorRole, AssistantRole)] 
    public ActionResult AdminOrAssistant() 
    {       
     return View(); 
    } 
} 

(Tôi viết blog về điều này trong một chút chi tiết hơn - http://tech-journals.com/jonow/2011/05/19/avoiding-magic-strings-in-asp-net-mvc-authorize-filters)

+0

Làm thế nào bạn có thể tùy chỉnh điều này hơn nữa làm cho nó trở thành một chức năng hoặc đại biểu? tức là người dùng => user.Role == AssistantRole || user.Role == BigGuy ... Một số hành động có thể muốn một vai trò và không phải vai trò khác, một số có thể muốn 2 vai trò hoặc vai trò thứ 3, tôi hy vọng tôi rõ ràng ??? :) – Haroon

3

tôi đã phản ứng JohnnyO nhưng thay đổi kiểu liệt kê các mục để sử dụng DescriptionAttribute để chỉ định giá trị chuỗi cho vai trò. Điều này có ích nếu bạn muốn chuỗi vai trò của bạn khác với tên Enum.

Ví dụ enum:

[Flags] 
public enum AppRole 
{ 
    [Description("myRole_1")] 
    RoleOne = 1, 
    [Description("myRole_2")] 
    RoleTwo = 2 
} 

Các phương pháp khuyến nông:

public static bool IsInRole(this IPrincipal user, AppRole appRole) 
{ 
    var roles = new List<string>(); 
    foreach (var role in (AppRole[])Enum.GetValues(typeof(AppRole))) 
     if ((appRole & role) != 0) 
      roles.Add(role.ToDescription()); 

    return roles.Any(user.IsInRole); 
} 

Thuộc tính tùy chỉnh:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 
public class AppAuthorizeAttribute : AuthorizeAttribute 
{ 
    public AppRole AppRoles { get; set; } 

    public override void OnAuthorization(AuthorizationContext filterContext) 
    { 
     var roles = new List<string>(); 
     foreach (var role in (AppRole[])Enum.GetValues(typeof(AppRole))) 
      if((AppRoles & role) != 0) 
       roles.Add(role.ToDescription()); 

     if (roles.Count > 0) 
      Roles = string.Join(",", roles); 

     base.OnAuthorization(filterContext); 
    } 
} 

mở rộng phương pháp để có được giá trị mô tả:

public static string ToDescription(this Enum value) 
{ 
    var da = (DescriptionAttribute[]) 
      (value.GetType().GetField(value.ToString())) 
       .GetCustomAttributes(typeof (DescriptionAttribute), false); 
    return da.Length > 0 ? da[0].Description : value.ToString(); 
} 
Các vấn đề liên quan