Возможно, я должен скопировать и расширить объем прежде, чем погрузиться в вопрос о заголовке...
Я в настоящее время пишу веб-приложение в ASP.NET MVC 1.0 (хотя мне действительно устанавливали MVC 2.0 на моем ПК, таким образом, я точно не ограничиваюсь 1,0) - я запустил со стандартного проекта MVC, который имеет Ваше основное, "Добро пожаловать в ASP.NET MVC", и показывает и [Домашнюю] вкладку и вкладку [About] в верхнем правом углу. Довольно стандартный, правильно?
Я добавил 4 новых Класса контроллера, давайте назовем их "Астрономом", "Биологом", "Химиком" и "Физиком". Присоединенный к каждому новому классу контроллера [Авторизовать] атрибут.
Например, для BiologistController.cs
[Authorize(Roles = "Biologist,Admin")]
public class BiologistController : Controller
{
public ActionResult Index() { return View(); }
}
Они [Авторизовывают] теги, естественно ограничивают, какой пользователь может получить доступ к различным контроллерам в зависимости от Ролей, но я хочу динамично создать Меню наверху своего веб-сайта в Сайте. Ведущее устройство Page на основе Ролей пользователь является частью. Так, например, если бы "JoeUser" был членом Ролей "Астроном" и "Физик", то в навигационном меню было бы сказано:
[Домашний] [Астроном] [Физик] [О]
И естественно, это не перечислило бы ссылки на Индексную страницу контроллера "Биолога" или "Химика".
Или если бы "JohnAdmin" был членом Роли "Администратор", то ссылки на все 4 контроллера обнаружились бы в панели навигации.
Хорошо, Вы пролил получаете идею... Теперь для реального вопроса...
Начиная с ответа от этой темы StackOverflow о здании Динамического меню в ASP.NET, я пытаюсь понять, как я полностью реализовал бы это.
Ответ предлагает Расшириться, Класс контроллера (назовите его "ExtController"), и затем имейте каждый новый WhateverController, наследовались ExtController.
Мое заключение состоит в том, что я должен был бы использовать Отражение в этом Конструкторе ExtController для определения, который имеют Классы и Методы [Разрешают] атрибуты, присоединенные к ним определять Роли. Затем с помощью Статического Словаря, сохраните Роли и Контроллеры/Методы в парах "ключ-значение".
Я воображаю это чем-то вроде этого:
public class ExtController : Controller
{
protected static Dictionary> ControllerRolesDictionary;
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
// build list of menu items based on user's permissions, and add it to ViewData
IEnumerable
Действительно ли это выполнимо? Если так, как я выполняю Отражение в конструкторе ExtController для обнаружения [Авторизовать] атрибута и связанных Ролей (если таковые имеются)
ТАКЖЕ! Не стесняйтесь выходить по этому вопросу и предлагать альтернативный способ решить этот "Динамический Сайт. Главное меню на основе Ролей" проблема. Я являюсь первым, чтобы признать, что это не может быть лучшим подходом.
Итак, я решил доработать свой собственный класс расширенного контроллера, как я изначально предлагал. Вот очень базовая версия. Я вижу различные способы сделать это лучше (расширить, ужесточить код и т.д.), но я решил предложить свои основные результаты, потому что я представляю, что есть много других людей, которые хотят что-то подобное, но, возможно, не хотят всех дополнительных возможностей.
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,""));
}
}
}
}
}
Чтобы использовать, просто:
Например:
[Authorize(Roles = "Biologist,Admin")]
public class BiologistController : ExtController
{
public ActionResult Index()
{ return View(); }
}
Если вы не замените "Controller" на "ExtController", то у этого контроллера не будет динамического меню. (Это может быть полезно, в некоторых сценариях, я думаю...)
В моем Site.Master файле, я изменил "menu" секцию, чтобы она выглядела так:
<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>
И это все! :-)
Я предпочитаю ставить ссылки на все в меню и создавать HtmlHelper, который проверяет, является ли ссылка доступной или нет, основываясь на атрибутах [Authorize].