2010-05-13 3 views
25

ASP.NET MVC는 역할 기반 보안을 훌륭하게 지원하지만 문자열을 역할 이름으로 사용하는 것은 열거 형으로 강력하게 유형 지정 될 수 없기 때문에 미친 듯합니다.ASP.NET MVC의 문자열이 아닌 역할 이름은 무엇입니까?

예를 들어, 내 앱에 "Admin"역할이 있습니다. "관리자"문자열은 이제 내 작업의 권한 부여 특성, 마스터 페이지 (탭 숨김), 데이터베이스 (각 사용자가 사용할 수있는 역할 정의 용) 및 다른 곳에서 볼 수 있습니다 관리 또는 비 관리자 사용자를 위해 특수 논리를 수행해야하는 파일

열거 형 값의 집합을 처리 할 수있는 내 자신의 인증 특성 및 필터를 작성하는 것이 더 나은 해결책이 있습니까?

답변

19

일반적으로 문자열 상수가 많은 클래스를 사용합니다. 완벽한 솔루션이 아닙니다. 어디서나 사용하는 것을 기억해야하기 때문에, 적어도 오타가 발생할 가능성은 없애줍니다.

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

단순함으로이 솔루션을 사용했습니다. 하드 코딩 된 문자열을 상수 참조로 바꾸기 만하면되므로 코드 변경이 최소화되었습니다. – MikeWyatt

3

그것은 당신이 제안하는 방식에 AuthorizeAttribute을 사용자 정의 할 수 어려운 일이 아니다.

하위 형식을 지정하고 열거 형에 대한 사용자 지정 속성을 추가하고 전달 된 값으로 ToString()을 호출하십시오. 일반 역할 속성에 넣으십시오. 이것은 단지 몇 줄의 코드를 필요로하며, AuthorizeAttribute은 여전히 ​​모든 실제 작업을 수행합니다.

+1 Matti 역시 const가 좋은 선택이기 때문에 Matti도 마찬가지입니다.

2

Matti가 제안한 문자열 상수 묶음을 정의하는 정적 클래스를 사용했으며 현재 프로젝트에서 열거 형으로 아래 확장 메서드를 사용합니다. 두 방법 모두 잘 작동합니다. 마법 문자열을 사용하여 당신에게 권한 부여 속성에 여러 역할을 선언 할 수있는 유연성 (예를 들어 [권한 부여 (역할 = "관리자, 중재자")] 당신은 당신이 강력한 형식의 솔루션에 가서 잃는 경향이 있습니다

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

. 그러나 여기 여전히 강력한 형식의 모든 것을 가져 오는 동안이 유연성을 유지할 수있는 방법은

는 비트 플래그 사용하는 열거에서 역할을 정의합니다.

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

재정 AuthorizeAttribute을 :

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

} 

지금 당신은이 같은 MyAuthorizeAttribute를 사용할 수있는 경우 : 위의 조치는 나열된 역할 중 하나 이상 (관리자, 중재자, 또는 편집기)에있는 사용자에게 권한을 부여합니다

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

    return View(); 
} 

. MVC의 기본 AuthorizeAttribute와 동일하지만 마법 문자열은 제외됩니다.이 기술을 사용하는 경우

, 여기에 유용 할 수 있습니다 IPrincipal에 확장 메서드는 다음과 같습니다 그렇지 않지만

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

    if (!allowed) { 
     // Do Something 
    } 

    return View(); 
} 
+0

나는이 접근법을 정말 좋아하고 구현하고 유지하기가 쉽다. – Sam

+0

@Jammer, 열거 형 값은 반드시 데이터베이스 ID와 일치 할 필요는 없다. 그들은 독립적 일 수 있으며 여전히 잘 작동합니다. –

+0

그래, 내가 그걸 사용하기 시작했다는 것을 깨달았다. 바보 같은 ... – Jammer

10

:

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

는이 같은이 확장 방법을 사용할 수 있습니다 열거 형을 사용합니다. 아래의 솔루션을 사용했습니다. 여기서 하위 클래스 인 Authorize 필터는 생성자에서 가변 길이 역할 이름 인수를 사용합니다. 어딘가에 CONST 변수에 선언 된 역할 이름과 함께이를 사용하여, 우리는 마법의 문자열을 피하기 :

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

(나는 조금 이것에 대해 더 자세히 블로그 - http://tech-journals.com/jonow/2011/05/19/avoiding-magic-strings-in-asp-net-mvc-authorize-filters)

+0

어떻게이 기능을 추가로 만들거나 위임 하시겠습니까? 즉 user => user.Role == AssistantRole || user.Role == BigGuy ... 어떤 행동은 다른 역할이 아닌 하나의 역할을 원할 수도 있고, 어떤 행동은 2 가지 역할 또는 제 3의 역할을 원할 수도 있습니다. 나는 분명히 희망합니다. ??? :) – Haroon

3

내가 JohnnyO의 응답을했다을하지만, 열거 변경 항목을 사용하여 DescriptionAttribute을 사용하여 역할의 문자열 값을 지정합니다. 역할 문자열을 열거 형 이름과 다르게하려면이 방법이 유용합니다.

열거 형 예 :

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

연장있어서

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

지정 특성 :

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(); 
} 
01 : 설명 값을 얻을

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

확장 방법