2010-06-08 1 views
9

어쩌면 제목 질문으로 뛰어 가기 전에 범위를 백업하고 넓혀야 할 것입니다 ...ASP.NET MVC : C#의 Reflection을 사용하여 [Authorize] 속성을 사용하여 컨트롤러를 찾는 방법은 무엇입니까? (또는 Dynamic Site.Master 메뉴를 만드는 방법)

현재 ASP.NET MVC에서 웹 응용 프로그램을 작성하고 있습니다. 1.0 (내 컴퓨터에 MVC 2.0이 설치되어 있기 때문에 1.0으로 제한되지 않습니다.) - 기본 ASP.NET MVC에 오신 것을 환영합니다. [홈] 탭과 오른쪽 상단의 [정보] 탭을 클릭합니다. 꽤 표준 맞지?

저는 4 개의 새로운 컨트롤러 클래스를 추가했습니다. "천문학 자", "생물 학자", "화학자"및 "물리학 자"라고합시다. 새 컨트롤러 클래스마다 [Authorize] 특성이 첨부됩니다. BiologistController.cs

[Authorize(Roles = "Biologist,Admin")] 
public class BiologistController : Controller 
{ 
    public ActionResult Index() { return View(); } 
} 

이 [권한 부여]에 대한 예를 들어

는 역할에 따라 다른 컨트롤러에 액세스 할 수있는 사용자 제한 자연스럽게 태그,하지만 난 동적으로 내 웹 사이트의 상단에 메뉴를 구축하려는 사용자가 속한 역할을 기반으로하는 사이트. 마스터 페이지. 그래서 예를 들어, "JOEUSER은"역할 "천문학 자"와 "물리학"의 일원이었다, 탐색 메뉴는 말할 것입니다 :

[홈] [천문학] [물리학] [소개]

그리고 당연히, 그것은 이 아니며 목록은 "생물 학자"또는 "화학자"컨트롤러 색인 페이지로 연결됩니다.

"JohnAdmin"이 역할 "Admin"의 구성원 인 경우 네 개의 컨트롤러 모두에 대한 링크가 탐색 모음에 표시됩니다.

좋아, 당신은 prolly


나는 이것을 완전히 구현하는 것이 방법을 이해하기 위해 노력하고있어, the answer from this StackOverflow topic about Dynamic Menu building in ASP.NET 시작 ... 진짜 질문에 대한 ... 지금은 생각을. (저는 초보자이며 더 많은 안내가 필요합니다.)

대답은 컨트롤러 클래스 확장 ("ExtController"라고 함)을 제안하고 새로운 WhateverController를 ExtController에서 상속 받도록 제안합니다.

결론이 ExtController 생성자에서 Reflection을 사용하여 역할을 결정하기 위해 [Authorize] 특성이 연결된 클래스와 메서드를 결정해야합니다. 그런 다음 정적 사전을 사용하여 역할 및 컨트롤러/메소드를 키 - 값 쌍으로 저장하십시오.

public class ExtController : Controller 
{ 
    protected static Dictionary<Type,List<string>> ControllerRolesDictionary; 

    protected override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     // build list of menu items based on user's permissions, and add it to ViewData 
     IEnumerable<MenuItem> menu = BuildMenu(); 
     ViewData["Menu"] = menu; 
    } 

    private IEnumerable<MenuItem> BuildMenu() 
    { 
     // Code to build a menu 
     SomeRoleProvider rp = new SomeRoleProvider(); 
     foreach (var role in rp.GetRolesForUser(HttpContext.User.Identity.Name)) 
     { 

     } 
    } 

    public ExtController() 
    { 
     // Use this.GetType() to determine if this Controller is already in the Dictionary 
     if (!ControllerRolesDictionary.ContainsKey(this.GetType())) 
     { 
      // If not, use Reflection to add List of Roles to Dictionary 
      // associating with Controller 
     } 
    } 
} 

이 행할 :

은 내가 이런 걸 상상? 그렇다면 ExtController 생성자에서 Reflection을 수행하여 [Authorize] 속성 및 관련 역할 (있는 경우)을 검색하는 방법

또한! 자유롭게이 질문에 범위를 벗어나이 "동적 사이트. 역할 기반 마스터 메뉴"문제를 해결할 수있는 다른 방법을 제안하십시오. 나는 이것이 최선의 접근 방식이 아닐 수도 있다는 것을 처음으로 인정합니다.

편집

많은 독서와 실험 후, 나는 내 자신의 솔루션을 함께했다. 내 대답은 아래를 참조하십시오. 건설적인 피드백/비판을 환영합니다!

답변

3

좋아요, 그래서 원래 제안 된 것처럼 확장 컨트롤러 클래스를 살피기로 결정했습니다. 다음은 매우 기본적인 버전입니다. 나는 이것을 더 잘 만들 수있는 다양한 방법을 볼 수있다. (코드를 확장하는 등) 그러나 비슷한 결과를 원한다고 생각하는 사람들이 많지만 모든 것을 원할 수도 있다고 생각하기 때문에 기본적인 결과를 제공 할 것이라고 생각했다. 엑스트라들.

public abstract class ExtController : Controller 
{ 
    protected static Dictionary<string, List<string>> RolesControllerDictionary; 
    protected override void OnActionExecuted(ActionExecutedContext filterContext) 
    { 
     // build list of menu items based on user's permissions, and add it to ViewData 
     IEnumerable<MenuItem> menu = BuildMenu(); 
     ViewData["Menu"] = menu; 
    } 

    private IEnumerable<MenuItem> BuildMenu() 
    { 
     // Code to build a menu 
     var dynamicMenu = new List<MenuItem>(); 
     SomeRoleProvider rp = new SomeRoleProvider(); 
     // ^^^^^INSERT DESIRED ROLE PROVIDER HERE^^^^^ 
     rp.Initialize("", new NameValueCollection()); 
     try 
     { // Get all roles for user from RoleProvider 
      foreach (var role in rp.GetRolesForUser(HttpContext.User.Identity.Name)) 
      { // Check if role is in dictionary 
       if (RolesControllerDictionary.Keys.Contains(role)) 
       { 
        var controllerList = RolesControllerDictionary[role]; 
        foreach (var controller in controllerList) 
        { // Add controller to menu only if it is not already added 
         if (dynamicMenu.Any(x => x.Text == controller)) 
         { continue; } 
         else 
         { dynamicMenu.Add(new MenuItem(controller)); } 
        } 
       } 
      } 
     } 
     catch { } // Most role providers can throw exceptions. Insert Log4NET or equiv here. 
     return dynamicMenu; 
    } 

    public ExtController() 
    { 
     // Check if ControllerRolesDictionary is non-existant 
     if (RolesControllerDictionary == null) 
     { 
      RolesControllerDictionary = new Dictionary<string, List<string>>(); 
      // If so, use Reflection to add List of all Roles associated with Controllers 
      const bool allInherited = true; 
      const string CONTROLLER = "Controller"; 
      var myAssembly = System.Reflection.Assembly.GetExecutingAssembly(); 

      // get List of all Controllers with [Authorize] attribute 
      var controllerList = from type in myAssembly.GetTypes() 
           where type.Name.Contains(CONTROLLER) 
           where !type.IsAbstract 
           let attribs = type.GetCustomAttributes(allInherited) 
           where attribs.Any(x => x.GetType().Equals(typeof(AuthorizeAttribute))) 
           select type; 
      // Loop over all controllers 
      foreach (var controller in controllerList) 
      { // Find first instance of [Authorize] attribute 
       var attrib = controller.GetCustomAttributes(allInherited).First(x => x.GetType().Equals(typeof(AuthorizeAttribute))) as AuthorizeAttribute; 
       foreach (var role in attrib.Roles.Split(',').AsEnumerable()) 
       { // If there are Roles associated with [Authorize] iterate over them 
        if (!RolesControllerDictionary.ContainsKey(role)) 
        { RolesControllerDictionary[role] = new List<string>(); } 
        // Add controller to List of controllers associated with role (removing "controller" from name) 
        RolesControllerDictionary[role].Add(controller.Name.Replace(CONTROLLER,"")); 
       } 
      } 
     } 
    } 
} 

그냥 사용하려면

  • 는 컨트롤러 클래스
  • 에 [권한 부여 (역할 = ". SomeRole1, SomeRole2, SomeRole3 등"] 추가로 상속 "컨트롤러"를 교체 "ExtController"예를 들어

:.

[Authorize(Roles = "Biologist,Admin")] 
public class BiologistController : ExtController 
{ 
    public ActionResult Index() 
    { return View(); } 
} 

"컨트롤러"를 "ExtController"로 바꾸지 않으면 해당 컨트롤러에는 동적 메뉴가 없습니다. 내 Site.Master 파일에서

(이것은 ... 내가 생각하는 몇 가지 시나리오에서 유용 할 수 있음), I는 다음과 같이 할 수 "메뉴"섹션을 변경 :

<ul id="menu">    
    <li><%= Html.ActionLink("Home", "Index", "Home")%></li> 
    <% if (ViewData.Keys.Contains("Menu")) 
     { 
      foreach (MenuItem menu in (IEnumerable<MenuItem>)ViewData["Menu"]) 
      { %> 
    <li><%= Html.ActionLink(menu.Text, "Index", menu.Text)%></li>   
    <% } 
     } 
    %>  
    <li><%= Html.ActionLink("About", "About", "Home")%></li> 
</ul> 

그리고 그게 다야! :-)

+0

재배포 할 때까지 클래스가 변경되지 않으므로 ApplicationStart에서 Dictionary 를 빌드하는 것이 좋습니다. 그런 다음 메뉴를 만드는 것은 매번 동적 인 것이 아니라 사전에서 빠르게 조회합니다. –

3

[승인] 속성을 기반으로 내 메뉴 및 creating a HtmlHelper which checks to see if a link is accessible or not에있는 모든 항목에 연결하는 것을 선호합니다.

+0

답변을 살펴 봤는데, 내 무지를 용서해주십시오. 아직 ASP.NET MVC를 배우고 있습니다.하지만 폴더와 파일은 일반적으로이 2 개의 클래스를 고수 할 것입니까? 마찬가지로 SecurityTrimmedLink 클래스는 Site.Master 파일에서 어떻게 사용됩니까? – Pretzel

관련 문제