ASP.NET web api - 设置自定义 IIdentity 或 IPrincipal

Posted

技术标签:

【中文标题】ASP.NET web api - 设置自定义 IIdentity 或 IPrincipal【英文标题】:ASP.NET web api - Set custom IIdentity or IPrincipal 【发布时间】:2015-10-22 18:45:50 【问题描述】:

在我们的 asp.net mvc/web api 项目中,我们希望使用AuthorizeAttribute 自定义授权。我们注意到有两种不同的AuthorizeAttribute,一种在System.Web.MVC 命名空间中用于MVC,另一种在System.Net.Http 命名空间中用于Web api。

它在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?有没有关于它的设计模式?

相关问题 ASP.NET MVC - Set custom IIdentity or IPrincipal

【问题讨论】:

【参考方案1】:

我实现了一些东西作为概念验证,主要遵循以下内容:Authentication Filters in ASP.NET Web API 2

对于 Web API,您可以创建一个属性 IAuthenticationFilter。 如果我没记错的话,您可以将其作为过滤器添加到 WebApiConfig 中的全局过滤器中

config.Filters.Add(new YourAuthenticationAttribute());

或者您可以将其用作 api 控制器/方法的属性。

然后你可以实现AuthenticateAsync,获取请求的授权头,检查scheme,验证参数,如果都有效,设置principal。

我认为这个想法是您可以在一个链中添加多个这些过滤器,它们都可以根据一组特定的要求进行身份验证,例如他们寻找的方案,以及在链中设置主体的某个位置,或者挑战被返回。

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")]

除此之外我还没有使用过它,但希望这会为您指明正确的方向。

【讨论】:

谢谢!context.Principal正是我需要的,但我们的保护是基于asp.net web api,而不是web api 2,我无法实现接口IAuthenticationFilter。幸运的是,你的链接[ ASP.NET Web API 2 中的身份验证过滤器](asp.net/web-api/overview/security/authentication-filters) 对我有很大帮助,我在 asp.net web api Basic Authentication in ASP.NET Web API 中找到了另一种方法。它实现了一个 http 模块。 [Authorize(Roles = "Admin")] 给我“此请求的授权已被拒绝。”当我定义我的自定义授权时,它可以工作。公共类 MyCustomAuthorize : AuthorizationFilterAttribute 公共覆盖无效 OnAuthorization(HttpActionContext actionContext) if (!actionContext.ControllerContext.RequestContext.Principal.IsInRole("Admin")) actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized); base.OnAuthorization(actionContext); 最后,[MyCustomAuthorize] public class EmployeeController : ApiController 【参考方案2】:

这个AuthorizeAttribute 实现对我有用。它是为Http Basic Auth 设计的,但显然我也想从ApiController 内部获取User.Identity.IsAuthenticatedUser.Identity.Name,这很有效:

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);
    

【讨论】:

【参考方案3】:

我记得在 WebApi 中我只能使用 ControllerContext。但我不确定究竟是为什么。根据我的阅读,HttpContext 是线程静态的,但 WebApi 中的所有内容或多或少都是异步的。看看这个topic,说不定会回答一些问题。

【讨论】:

【参考方案4】:

只需扩展您的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


    

【讨论】:

以上是关于ASP.NET web api - 设置自定义 IIdentity 或 IPrincipal的主要内容,如果未能解决你的问题,请参考以下文章

asp.net core 2.0 web api基于JWT自定义策略授权

ASP.NET Web Api 路由自定义

asp.net web api添加自定义认证

如何自定义 ASP .NET Web API JWT 令牌响应?

ASP.NET WEB API 中自定义对象的序列化 [重复]

在asp.net的自定义类(C#)中怎样把变量输出到页面?