因此,当您有用户登录时,我正在阅读有关登录循环的另一个问题,设置为返回登录后可能无法访问的URL(即管理员页面,并且用户使用正常账户)。
WebForms下的解决方案似乎是使用UrlAuthorizationModule.CheckUrlAccessForPrincipal
方法。但是,对于使用授权属性保护的Action方法的URL不起作用。我想我可以找出URL指向哪个方法并反映它以解决我的问题 - 但我似乎无法弄清楚如何从路由表中获取此信息。
有人曾经使用过这个,或者有解决方案吗?如果我能从URL获取路由信息,我想我可以完成其余的工作,但是如果有人有一个通用的解决方案 - 即。一些隐藏的方法类似于前面提到的MVC,然后这也是非常棒的。
我不是问如何检查用户是否有权访问指定的控制器/操作对。我首先需要弄清楚如何根据URL从RouteTable获取Controller / Action对。所有背景故事的原因是,确实存在与MVC UrlAuthorizationModule.CheckUrlAccessForPrincipal
相当的。
答案 0 :(得分:8)
上面针对MVC 4更新了jfar的答案:
public static class SecurityCheck
{
public static bool ActionIsAuthorized(string actionName, string controllerName)
{
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
ControllerBase controller = factory.CreateController(HttpContext.Current.Request.RequestContext, controllerName) as ControllerBase;
var controllerContext = new ControllerContext(HttpContext.Current.Request.RequestContext, controller);
var controllerDescriptor = new ReflectedControllerDescriptor(controller.GetType());
var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
AuthorizationContext authContext = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (var authAttribute in actionDescriptor.GetFilterAttributes(true).Where(a => a is AuthorizeAttribute).Select(a => a as AuthorizeAttribute))
{
authAttribute.OnAuthorization(authContext);
if (authContext.Result != null)
return false;
}
return true;
}
}
答案 1 :(得分:5)
我从MvcSitemap移植并破解了这段代码:
public static class SecurityTrimmingExtensions
{
/// <summary>
/// Returns true if a specific controller action exists and
/// the user has the ability to access it.
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="actionName"></param>
/// <param name="controllerName"></param>
/// <returns></returns>
public static bool HasActionPermission( this HtmlHelper htmlHelper, string actionName, string controllerName )
{
//if the controller name is empty the ASP.NET convention is:
//"we are linking to a different controller
ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName)
? htmlHelper.ViewContext.Controller
: GetControllerByName(htmlHelper, controllerName);
var controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerToLinkTo);
var controllerDescriptor = new ReflectedControllerDescriptor(controllerToLinkTo.GetType());
var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
return ActionIsAuthorized(controllerContext, actionDescriptor);
}
private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (actionDescriptor == null)
return false; // action does not exist so say yes - should we authorise this?!
AuthorizationContext authContext = new AuthorizationContext(controllerContext);
// run each auth filter until on fails
// performance could be improved by some caching
foreach (IAuthorizationFilter authFilter in actionDescriptor.GetFilters().AuthorizationFilters)
{
authFilter.OnAuthorization(authContext);
if (authContext.Result != null)
return false;
}
return true;
}
private static ControllerBase GetControllerByName(HtmlHelper helper, string controllerName)
{
// Instantiate the controller and call Execute
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
IController controller = factory.CreateController(helper.ViewContext.RequestContext, controllerName);
if (controller == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
"Controller factory {0} controller {1} returned null",
factory.GetType(),
controllerName));
}
return (ControllerBase)controller;
}
它可以使用一些缓存,但对我来说这是一个过早的优化。
答案 2 :(得分:1)
这可能听起来有争议,但我在方法内部检查每个控制器方法开头的安全性:
public class ProductController : Controller
{
IProductRepository _repository
public ActionResult Details(int id)
{
if(!_repository.UserHasAccess(id))
return View("NotAuthorized");
var item = _repository.GetProduct(id);
if (item == null)
return View("NotFound");
return View(item);
}
}
我不使用[Authorize]
属性的原因是您无法在运行时将id或任何其他标识信息传递给属性。
答案 3 :(得分:1)
您要解决的问题是什么?听起来你可能正走向一条复杂的解决方案,而不是一个简单的解决方案。
如果用户在登录后无权访问该页面,您是否希望未登录的用户转到一个页面,而登录的用户则转到另一个页面?
如果是这种情况,我可能会想要为这样的场景创建另一个控制器,并在用户无法访问的任何地方重定向到该控制器。或者,如果您使用自己的基本控制器,我会将功能放在那里。
然后控制器可以呈现所需的视图。例如,如果未登录的用户尝试访问页面,则可能会将其重定向到通用错误页面。如果用户已登录,则可能会将其重定向到未授权的页面。
这与罗伯特的答案非常相似。
这是基本控制器的基本框架。
public BaseController: Controller
{
... // Some code
public ActionResult DisplayErrorPage()
{
// Assumes you have a User object with a IsLoggedIn property
if (User.IsLoggedIn())
return View("NotAuthorized");
// Redirect user to login page
return RedirectToAction("Logon", "Account");
}
}
然后让我们说一个AdminController(继承自BaseController)动作
public ActionResult HighlyRestrictedAction()
{
// Assumes there is a User object with a HasAccess property
if (User.HasAccess("HighlyRestrictedAction") == false)
return DisplayErrorPage();
// At this point the user is logged in and has permissions
...
}
答案 4 :(得分:1)
在我的应用程序中,我创建了一个从AuthorizeAttribute派生的自定义过滤器,因此任何未经授权的访问都只会转到AccessDenied页面。对于链接,我将Html.ActionLink替换为自定义帮助程序Html.SecureLink。在此帮助程序扩展中,我检查此用户的角色对数据库的控制器/操作的访问权限。如果他/她有授权,则返回链接否则返回带有特殊备注的链接文本(可能是image / coloring / js)
答案 5 :(得分:0)
为什么不将控制器方法归于安全要求。
我写了一个属性,如下所示:
public class RequiresRoleAttribute : ActionFilterAttribute
{
public string Role { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (string.IsNullOrEmpty(Role))
{
throw new InvalidOperationException("No role specified.");
}
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
else
{
bool isAuthorised = filterContext.HttpContext.User.IsInRole(this.Role);
<< Complete Logic Here >>
}
}
}
答案 6 :(得分:0)
我只是花了一些时间来实现@jfar的解决方案(为非弃用的GetFilters()版本更新它),然后我意识到我可以跳过这一切。
在我的情况下(我假设大多数情况下)我有一个自定义AuthorizationAttribute来实现站点授权,然后调用我的授权服务来进行实际的访问级别确定。
所以在用于生成菜单链接的html帮助器中,我跳过了auth服务:
@Html.MenuItem(@Url, "icon-whatever", "TargetController", "TargetAction")
public static MvcHtmlString MenuItem(this HtmlHelper htmlHelper, UrlHelper url,string iconCss, string targetController, string targetAction)
{
var auth = IoC.Resolve<IClientAuthorizationService>().Authorize(targetController, targetAction);
if (auth == AccessLevel.None)
return MvcHtmlString.Create("");
*用户在客户端身份验证服务中确定
public string GetUser() {
return HttpContext.Current.User.Identity.Name;
}
*还可以为只读访问添加一些行为。这很好,因为我的auth服务负责缓存,所以我不必担心性能。