Ala*_*an2 28 asp.net asp.net-mvc
现在我这样做是为了获得我需要的信息:
在我的基本控制器中:
public int roleId { get; private set; }
public int userId { get; private set; }
public void setUserAndRole()
{
ClaimsIdentity claimsIdentity;
var httpContext = HttpContext.Current;
claimsIdentity = httpContext.User.Identity as ClaimsIdentity;
roleId = Int32.Parse(claimsIdentity.FindFirst("RoleId").Value);
userId = Int32.Parse(User.Identity.GetUserId());
}
Run Code Online (Sandbox Code Playgroud)
在我的控制器方法中:
public async Task<IHttpActionResult> getTest(int examId, int userTestId, int retrieve)
{
setUserAndRole();
Run Code Online (Sandbox Code Playgroud)
我希望roleId和userId在我的类的构造函数中可用并填充,但是从我理解的构造函数在授权信息可用之前触发.
有人能告诉我如何使用Action Filter做到这一点吗?理想情况下,我希望Action Filter位于控制器级别,但如果没有,则可以在方法级别完成.
我希望得到一些好的建议和意见.谢谢
更新以显示System.Web.Http
#region Assembly System.Web.Http, Version=5.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// C:\H\server\packages\Microsoft.AspNet.WebApi.Core.5.2.2\lib\net45\System.Web.Http.dll
#endregion
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Controllers;
namespace System.Web.Http.Filters
{
//
// Summary:
// Represents the base class for all action-filter attributes.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IFilter
{
//
// Summary:
// Initializes a new instance of the System.Web.Http.Filters.ActionFilterAttribute
// class.
protected ActionFilterAttribute();
//
// Summary:
// Occurs after the action method is invoked.
//
// Parameters:
// actionExecutedContext:
// The action executed context.
public virtual void OnActionExecuted(HttpActionExecutedContext actionExecutedContext);
public virtual Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken);
//
// Summary:
// Occurs before the action method is invoked.
//
// Parameters:
// actionContext:
// The action context.
public virtual void OnActionExecuting(HttpActionContext actionContext);
public virtual Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken);
}
}
Run Code Online (Sandbox Code Playgroud)
Igo*_*gor 36
根据您的方法签名(以及后面的注释),代码假定您使用的是Web API而不是MVC,尽管这也可以轻松地针对MVC进行更改.
我想指出,如果你纯粹看看它的要求,我怎么能创建一个可重用的可维护代码片段.在这种情况下,代码获取基于声明的信息并将其注入您的控制器.您要求过滤器的事实是技术要求,但我也将提出一个不使用过滤器而是使用IoC的解决方案,这增加了一些灵活性(恕我直言).
一些技巧
System.Web.HttpContext.Current.很难对使用它的单元测试代码进行单元测试.Mvc和Web API有一个共同的抽象HttpContextBase,尽可能使用它.如果没有其他方法(我还没有看到),那么使用new HttpContextWrapper(System.Web.HttpContext.Current)并将此实例传递给您想要使用的任何方法/类(HttpContextWrapper派生自HttpContextBase).这些没有特别的顺序.请参阅end以获取每个解决方案的基本专业列表.
UserInfo.cs
这是我将在下面演示的两种解决方案中使用的常用代码.这是您希望访问的基于属性/声明的信息的常见抽象.这样,如果要添加对另一个属性的访问权限,只需扩展接口/类,就不必扩展方法.
using System;
using System.Security.Claims;
using System.Web;
using Microsoft.AspNet.Identity;
namespace MyNamespace
{
public interface IUserInfo
{
int RoleId { get; }
int UserId { get; }
bool IsAuthenticated { get; }
}
public class WebUserInfo : IUserInfo
{
public int RoleId { get; set; }
public int UserId { get; set; }
public bool IsAuthenticated { get; set; }
public WebUserInfo(HttpContextBase httpContext)
{
try
{
var claimsIdentity = httpContext.User.Identity as ClaimsIdentity;
IsAuthenticated = httpContext.User.Identity.IsAuthenticated;
if (claimsIdentity != null)
{
RoleId = Int32.Parse(claimsIdentity.FindFirst("RoleId").Value);
UserId = Int32.Parse(claimsIdentity.GetUserId());
}
}
catch (Exception ex)
{
IsAuthenticated = false;
UserId = -1;
RoleId = -1;
// log exception
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
此解决方案演示了您所要求的内容,这是一个可重用的Web API过滤器,用于填充基于声明的信息.
WebApiClaimsUserFilter.cs
using System.Web;
using System.Web.Http.Controllers;
namespace MyNamespace
{
public class WebApiClaimsUserFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// access to the HttpContextBase instance can be done using the Properties collection MS_HttpContext
var context = (HttpContextBase) actionContext.Request.Properties["MS_HttpContext"];
var user = new WebUserInfo(context);
actionContext.ActionArguments["claimsUser"] = user; // key name here must match the parameter name in the methods you want to populate with this instance
base.OnActionExecuting(actionContext);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在,您可以通过将此过滤器应用于Web API方法(如属性或类级别)来使用此过滤器.如果您想在任何地方访问,您也可以将其添加到WebApiConfig.cs代码中(如果可选).
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new WebApiClaimsUserFilterAttribute());
// rest of code here
}
}
Run Code Online (Sandbox Code Playgroud)
WebApiTestController.cs
这里有如何在Web API方法中使用它.请注意,匹配是基于参数名称完成的,这必须与过滤器中指定的名称匹配actionContext.ActionArguments["claimsUser"].现在,您的方法将使用过滤器中添加的实例进行填充.
using System.Web.Http;
using System.Threading.Tasks;
namespace MyNamespace
{
public class WebApiTestController : ApiController
{
[WebApiClaimsUserFilterAttribute] // not necessary if registered in webapiconfig.cs
public async Task<IHttpActionResult> Get(IUserInfo claimsUser)
{
var roleId = claimsUser.RoleId;
await Task.Delay(1).ConfigureAwait(true);
return Ok();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个关于控制反转的维基和关于依赖注入的维基.这些术语IoC和DI通常可互换使用.简而言之,您可以定义依赖项,使用DI或IoC框架注册它们,然后将这些依赖项实例注入到运行的代码中.
有很多IoC框架,我使用AutoFac,但你可以使用你想要的任何东西.按照这种方法,您可以定义一次注射,并随时随地访问它们.只需在构造函数中引用我的新接口,它就会在运行时注入实例.
DependencyInjectionConfig.cs
using System.Reflection;
using System.Web.Http;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using Autofac.Integration.WebApi;
namespace MyNamespace
{
public static class DependencyInjectionConfig
{
/// <summary>
/// Executes all dependency injection using AutoFac
/// </summary>
/// <remarks>See AutoFac Documentation: https://github.com/autofac/Autofac/wiki
/// Compare speed of AutoFac with other IoC frameworks: http://nareblog.wordpress.com/tag/ioc-autofac-ninject-asp-asp-net-mvc-inversion-of-control
/// </remarks>
public static void RegisterDependencyInjection()
{
var builder = new ContainerBuilder();
var config = GlobalConfiguration.Configuration;
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterControllers(typeof(DependencyInjectionConfig).Assembly);
builder.RegisterModule(new AutofacWebTypesModule());
// here we specify that we want to inject a WebUserInfo wherever IUserInfo is encountered (ie. in a public constructor in the Controllers)
builder.RegisterType<WebUserInfo>()
.As<IUserInfo>()
.InstancePerRequest();
var container = builder.Build();
// For Web API
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// 2 lines for MVC (not web api)
var resolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolver);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们只需要在应用程序启动时调用它,这可以在Global.asax.cs文件中完成.
using System;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.Http;
namespace MyNamespace
{
public class Global : HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
DependencyInjectionConfig.RegisterDependencyInjection();
// rest of code
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以在任何我们想要的地方使用它.
WebApiTestController.cs
using System.Web.Http;
using System.Threading.Tasks;
namespace MyNamespace
{
public class WebApiTestController : ApiController
{
private IUserInfo _userInfo;
public WebApiTestController(IUserInfo userInfo)
{
_userInfo = userInfo; // injected from AutoFac
}
public async Task<IHttpActionResult> Get()
{
var roleId = _userInfo.RoleId;
await Task.Delay(1).ConfigureAwait(true);
return Ok();
}
}
}
Run Code Online (Sandbox Code Playgroud)
以下是您可以从NuGet获取此示例的依赖项.
Install-Package Autofac
Install-Package Autofac.Mvc5
Install-Package Autofac.WebApi2
Run Code Online (Sandbox Code Playgroud)
我想到的另一种解决方案.您从未指定过为什么需要用户和角色ID.也许您想在继续之前检查方法中的访问级别.如果是这种情况,最好的解决方案是不仅要实现过滤器,还要创建覆盖System.Web.Http.Filters.AuthorizationFilterAttribute.这允许您在代码执行之前执行授权检查,如果您在Web api界面上具有不同的访问级别,则非常方便.我放在一起的代码说明了这一点,但您可以扩展它以添加对存储库的实际调用以进行检查.
WebApiAuthorizationClaimsUserFilterAttribute.cs
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http.Controllers;
namespace MyNamespace
{
public class WebApiAuthorizationClaimsUserFilterAttribute : System.Web.Http.Filters.AuthorizationFilterAttribute
{
// the authorized role id (again, just an example to illustrate this point. I am not advocating for hard coded identifiers in the code)
public int AuthorizedRoleId { get; set; }
public override void OnAuthorization(HttpActionContext actionContext)
{
var context = (HttpContextBase) actionContext.Request.Properties["MS_HttpContext"];
var user = new WebUserInfo(context);
// check if user is authenticated, if not return Unauthorized
if (!user.IsAuthenticated || user.UserId < 1)
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "User not authenticated...");
else if(user.RoleId > 0 && user.RoleId != AuthorizedRoleId) // if user is authenticated but should not have access return Forbidden
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden, "Not allowed to access...");
}
}
}
Run Code Online (Sandbox Code Playgroud)
WebApiTestController.cs
using System.Web.Http;
using System.Threading.Tasks;
namespace MyNamespace
{
public class WebApiTestController : ApiController
{
[WebApiAuthorizationClaimsUserFilterAttribute(AuthorizedRoleId = 21)] // some role id
public async Task<IHttpActionResult> Get(IUserInfo claimsUser)
{
// code will only be reached if user is authorized based on the filter
await Task.Delay(1).ConfigureAwait(true);
return Ok();
}
}
}
Run Code Online (Sandbox Code Playgroud)
AuthorizationFilterAttribute是最佳方法.如果授权通过,您可以将解决方案#1中的过滤器中的代码添加到此代码中,这样您仍然可以在代码中访问用户信息以用于其他目的.创建自定义ActionFilter类(用于OnActionExecuting):
using System.Security.Claims;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
namespace YourNameSpace
{
public class CustomActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
ClaimsIdentity claimsIdentity = HttpContext.Current.User.Identity as ClaimsIdentity;
filterContext.ActionParameters["roleId"] = int.Parse(claimsIdentity.FindFirst("RoleId").Value);
filterContext.ActionParameters["userId"] = int.Parse(claimsIdentity.GetUserId());
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后装饰一系列基本控制器,控制器或操作(取决于您要应用自定义过滤器的级别),并将roleId和userId指定为操作参数:
[CustomActionFilter]
public async Task<IHttpActionResult> getTest(int roleId, int userId, int examId, int userTestId, int retrieve)
{
// roleId and userId available to use here
// Your code here
}
Run Code Online (Sandbox Code Playgroud)
希望应该这样做.
| 归档时间: |
|
| 查看次数: |
23385 次 |
| 最近记录: |