在我们的asp.net mvc / web api项目中,我们希望使用AuthorizeAttribute
自定义授权。我们注意到有两个不同的AuthorizeAttribute
,一个用于MVC的System.Web.MVC
命名空间,另一个用于web api的System.Net.Http
命名空间。
它适用于MVC,我们的代码如下:
public class MyPrincipal : IPrincipal
{
//some custom properties
public bool IsValid()
{
//custom authentication logic
}
private IIdentity identity;
public IIdentity Identity
{
get { return this.identity; }
}
public bool IsInRole(string role)
{
return true;
}
}
//override AuthorizeCore
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
MyPrincipal user = new MyPrincipal();
if (user.isValid())
{
httpContext.User = user;
}
else
{
httpContext.Response.Redirect("~/Common/NoAuthorize", true);
}
}
}
[MyAuthorizeAttribute]
public class BaseMyController : Controller
{
protected virtual new MyPrincipal User
{
get { return HttpContext.User as MyPrincipal; }
}
}
然后在MVC控制器中,我们可以通过MyPrincipal
用户属性获取用户信息。
但是,当我们开始在web api中使用相同的方式时,我们发现web api没有HttpContext
属性,而在System.Web.Http.AuthorizeAttribute
中,要覆盖的方法接受HttpActionContext
}参数,它也没有HttpContext
属性或我们可以设置MyPrincipal
实例的其他地方。
我注意到System.Web.Http.AuthorizeAttribute
摘要说
指定验证请求的IPrincipal
的授权过滤器
似乎还有其他方法来设置IPrincipal
实例。
我不知道,有什么好建议吗?那么,为什么asp.net web api控制器没有HttpContext
?有关于它的设计模式吗?
答案 0 :(得分:4)
我实施了一些概念证据,主要是:Authentication Filters in ASP.NET Web API 2
对于Web API,您可以创建一个Attribute,IAuthenticationFilter。 如果我没记错的话,你可以将它作为过滤器添加到WebApiConfig
中的全局过滤器中config.Filters.Add(new YourAuthenticationAttribute());
或者您可以将其用作api控制器/方法的属性。
然后,您可以实现AuthenticateAsync,获取请求的授权标头,检查方案并验证参数,如果一切都有效,则设置主体。
我认为这个想法是你可以在一个链中添加多个这些过滤器,它们都可以针对一组特定的需求进行身份验证,比如他们寻找的方案,以及主要设置链中的某个位置,或者挑战被退回。
public class YourAuthenticationAttribute : Attribute, IAuthenticationFilter
{
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
if (request.Headers.Authorization != null &&
request.Headers.Authorization.Scheme.Equals("yourScheme", StringComparison.OrdinalIgnoreCase))
{
// get the value sent with the header.
string authParam = request.Headers.Authorization.Parameter;
// do some validation on the parameter provided...
// if it's all valid, create a principal with claims:
List<Claim> claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, "Eddie Admin"),
new Claim(ClaimTypes.Role, "Admin"),
// new Claim(ClaimTypes.Role, "Delete"),
};
// create an identity with the valid claims.
ClaimsIdentity identity = new ClaimsIdentity(claims, "yourScheme");
// set the context principal.
context.Principal = new ClaimsPrincipal(new[] { identity });
创建主体时,您可以应用声明,并根据常规授权属性检查这些声明。例如
[Authorize(Roles = "Admin")]
除了这样做之外,我还没有使用它,但希望这能指出你正确的方向。
答案 1 :(得分:0)
我记得在WebApi中我只能使用ControllerContext。但我不确定为什么。从我读到的内容来看,HttpContext是线程静态的,但WebApi中的所有内容都或多或少都是异步的。看看这个topic,也许它会回答一些问题。
答案 2 :(得分:0)
只需扩展您的IPrincipal
。
public static class IPrincipalExtension
{
public static bool IsValid(this IPrincipal p)
{
// your custom validation
return true;
}
}
现在引用您的命名空间并像这样使用。
public class MyAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var valid = actionContext.RequestContext.Principal.IsValid(); // this will return boolean
// your code here
}
}
答案 3 :(得分:0)
此AuthorizeAttribute
实施对我有用。它是专为Http Basic Auth而设计的,但显然我想从User.Identity.IsAuthenticated
内部获取User.Identity.Name
和ApiController
这样做有效:
public class ApiAuthAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var session = (ISession)actionContext.Request.GetDependencyScope().GetService(typeof(ISession));
if (actionContext.Request.Headers.Authorization != null)
{
var authConcat = Encoding.UTF8.GetString(Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter));
var email = authConcat.Split(':')[0];
var password = authConcat.Split(':')[1];
var user = session.Query<UserAccount>().SingleOrDefault(u => u.Email == email);
if (user != null && user.IsAuthenticated(password))
{
actionContext.ControllerContext.RequestContext.Principal = new GenericPrincipal(new GenericIdentity(user.Email), new string[] { });
return; // and continue with controller
}
}
actionContext.Response = new HttpResponseMessage(HttpStatusCode.NotFound);
}
}