ASP.NET Web API的安全管道

 

本篇體驗ASP.NET Web API的安全管道。這裏的安全管道是指在請求和響應過程當中所經歷的各個組件或進程,好比有IIS,HttpModule,OWIN,WebAPI,等等。在這個管道中大體分兩個階段,一個是驗證階段,另外一個是受權階段。

在ASP.NET Web API v1版本的時候,安全管道大體是這樣的:

→ Authentication,請求來到IIS中的HttpModule
→ Authenticatin, 請求來到API的HttpMessageHandler
→ Authorization, 請求來到Authorization Filter
→ Authorization, 請求來到Controller

當ASP.NET Web API來到v2版本的時候,安全管道大體是:

→ 請求來到Host中的OWIN組件
→ 請求來到MessageHandler,全局或按每一個請求
→ 請求來到Authentication Filter
→ 請求來到Authorization Filter

可見,加入了OWIN組件,OWIN是開源的, Microsoft在此基礎上開發出了Katana驗證中間件。

咱們知道,ASP.NET Web API的宿主有兩種方式:

一、Web宿主,ASP.NET, IIS
二、自宿主,WCF,.NET進程

若是把OWIN考慮進去,那就是:
一、IIS→ASP.NET+OWIN Bridge→ OWIN→Web API + OWIN Adapter
二、Process/Host+OWIN Bridge→OWIN→Web API + OWIN Adapter

html

1、瞭解管道中的各個組件


1.1 OWIN中間件web

 

public class AuthenticationMiddleware
{
    private readonly Func<IDictionary<string, object>, Task> _next;
    
    public AuthenticationMiddleware(Func<IDictionary<string, object>, Task> next)
    {
        _next = next;
    }
    
    public async Task Invoke(IDictionary<string, object> env)
    {
        //檢查env集合,進行驗證
        env["server.user"] = CreatePrincipal();//設置principal;
        await _next(env);
    }
}

 

OWIN中間件的大體工做原理是:請求中的Header,Body,路由等信息被放在了IDictionary<string, object>這個字典集合中,而且提供了Invoke方法,把獲取到的用戶信息放在env["server.user"]中,而且調用一個動做處理IDictionary<string, object>集合。


1.2 Katana Authentication Middleware

這是Microsoft基於OWIN開發出來的驗證組件,大體是:api

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCookieAuthentication(new CookieAuthenticaitonOptions{
            AuthenticationType = "Cookies",
            //more
        });
        
        app.UseGoogleAuthentication(new GoogleAuthenticationOptions{
            AuthenticationType = "Google";
            //more
        });
        
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions{
            AuthenticationType = "Bearer";
            // more
        })
    }
}

以上,至少能夠看出,能夠爲OWIN組件選擇驗證方式。

1.3 Message Handler

實施在全局或某個請求上。大體是:數組

public class MyHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        //檢查請求
        
        var response = await base.SendAsync(request, cancellationToken);
        
        //檢查響應
        return response;
    }
}

 

Message Handler從ASP.NET WEB API 2之後就不存在了。安全

 

1.4 Authentication Filter 驗證過濾器

能夠在全局配置:

WebApiConfig.cs
config.Filters.Add(new HostAuthenticationFilter("Bearer"));

固然過濾器也能夠放在控制器和方法層面:app

[HostAuthentication("Bearer")]
public class TestController : ApiController
{
    [HostAuthentication("Google")]
    public HttpResponseMessage Get(){}
    
    [OverrideAuthentication]
    [HostAuthentication("Cookies")]
    public HttpResponseMessage Delete(){}
}

 

1.5 Authorization Filter 受權過濾器dom

 

[Authorize]
public class DataController : ApiController
{
    [AllowAnonymous]
    public Data Get(){}
    
    [Authorize(Role = "Foo")]
    public HttpResponseMessage Delete(int id){}
}

若是受權失敗,返回401報錯。

1.6 獲取用戶的Identity

經過ApiController的User屬性獲取到用戶的Identity。注意,User屬性值可能爲null。

async

2、經過例子來體驗安全管道


2.1 自定義HttpModule

首先,請求過來,確定要經過HttpModule。咱們須要自定義一個HttpModule,經過一個版主方法把當前的用戶信息打印出來。ide

 

namespace SecurityPipeline
{
    public class HttpModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.BeginRequest += context_BeginRequest;
        }
        
        void context_BeginRequest(object sender, EventArgs e)
        {
            Helper.Write("HttpModule", HttpContext.Current)
        }
        
        public void Dispose()
        {
        
        }
    }
}

namespace SecurityPiepeline
{
    public static class Helper
    {
        public static void Write(string state, IPrincipal principal)
        {
            Debug.WriteLine("------------" + stage + "--------");
            if(principal == null || principal.Identity == null  || !principal.Identity.IsAuthenticated)
            {
                Debug.WriteLine("anonymous user");
            }
            else
            {
                Debug.WriteLine("User:" + principal.Identity.User);
            }
            
            Debug.WriteLine("\n");
        }
    }
}

 

可見,HttpContext.Current是IPrincipal類型。

而後這是一個Web項目,須要把HTTP module註冊一下。ui

 

<configuration>
    <system.webServer>
        <modules>
            <add name="DemoModule" type="SecurityPipeline.HttpModule"/>
        </modules>
    </system.webServer>
</configuration>

 

若是此時項目下有一個default.html頁面的話,運行項目,展現default.html的時候,控制檯打印出以下信息:

-----HttpModule-------
anonymouse

顯然,請求過來,自定義的Http Module起了做用,但目前還不能從IPrincipal中拿到User信息。

2.2 安裝ASP.NET Web API 2

2.3 建立控制器

 

using System.Net.Http;
public class TestController : ApiController
{
    public IHttpActionResult Get()
    {
        Helper.Write("Controller", User);
        
        //獲取用戶也能夠這樣寫
        //Helper.Write("Controller",Request.GetRequestContext().Principal);
        return Ok();
    }
}

 

以上,經過ApiController的User屬性獲取到IPincipal類型。

2.4 安裝Microsoft.Owin.Host.SystemWeb

2.5 安裝Microsoft. ASP.NET Web API 2.1 OWIN

2.6 建立Startup類

 

using OWin;
using System.Web.Http;

namespace SecurityPopeline
{
    public class Startup
    {
        public void Configuraiton()
        {
            var configuration = new HttpConfiguration();
            configuration.Routes.MapHttpRoute("default", "api/{controller}");
            
        }
    }
}

這裏,讓WEB API的HttpConfiguraton實例賦值給類OWIN的IAppBuilder的UseWebApi方法。

2.7 請求路由:localhsot:8000/api/test

顯示
------HttpModule--------
anonymous user

------Controller--------
anonymous user

可見,請求一路通過管道中的HttpModule和Controller,但依然沒有拿到用戶信息。

2.8 建立TestMiddleware類

進入HttpModule以後,和進入Controller以前,這裏是OWIN組件的生存之地。

 

using Microsoft.Owin;

namespace SecurityPopeline.Pipeline
{
    public class TestMiddleware
    {
        private Func<IDictionary<string, object>, Task> _next;
        public TestMiddleware(Func<IDictionary<string, object>, Task> next)
        {
            _next = next;
        }
        
        public async Task Invoke(IDictionary<string, object> env)
        {
            var context = new OwinContext(env);
            Helper.Write("Middleware", context.Request.User);
            await _next(env);
        }
    }
}

 

2.9 Startup類中增長有關TestMiddleware部分

 

using OWin;
using System.Web.Http;

namespace SecurityPopeline
{
    public class Startup
    {
        public void Configuraiton(IAppBuilder app)
        {
            var configuration = new HttpConfiguration();
            configuration.Routes.MapHttpRoute("default", "api/{controller}");
            
            app.Use(typeo(TestMiddleware));
            
            app.UseWebApi(configuration);
        }
    }
}

 

3.10 請求路由:localhsot:8000/api/test

顯示
------HttpModule--------
anonymous user

------Middleware--------
anonymous user

------Controller------
anonymous user

可見,請求一路過來歷經管道中的HttoModule, OWIN, 最後到達Controller,依然沒有獲取到用戶信息。

3.11 添加TestAuthenticationFilterAttribute類

在OWIN和Controller之間,還有驗證的接口,這也是安全管道中的一個重要環節。

 

using System.Web.Http.Filters;
using System.Threading.Tasks;

namespace SecurityPipeline.Pipeline
{
    public class TestAuthenticationFilterAttribute : Attribute, IAuthenticationFilter
    {
        public async Task AuthenticateAsync(HttpAuthenticationContext context)
        {
            Helper.Write("AuthenticationFilter", context.ActionContext.RequestContext.Principal, CancellationToken..)
        }
        
        public async Task ChallengeAsync(HttpAuthenticationContext context, CancellationToken..)
        {
        
        }
        
        public bool AllowMultiple
        {
            get {
                return false;
            }
        }
    }
}

 

控制器增長過濾特性

 

using System.Net.Http;


[TestAuthenticationFilter]
public class TestController : ApiController
{
    public IHttpActionResult Get()
    {
        Helper.Write("Controller", User);
        
        //獲取用戶也能夠這樣寫
        //Helper.Write("Controller",Request.GetRequestContext().Principal);
        return Ok();
    }
}

 

3.12 請求路由:localhsot:8000/api/test

顯示
------HttpModule--------
anonymous user

------Middleware--------
anonymous user

------AuthenticationFilter--------
anonymous user

------Controller------
anonymous user

可見,請求路徑安全管道中的HttpModule,OWIN,驗證,依然沒有獲取到用戶信息。

3.13 增長TestAuthorizationFilterAttrbute類

在通過驗證特性,以及進入Controller或Action以前,安全管道中還有一個重要的成員,就是受權特性。

 

public class TestAuthorizationFilterAttribute : AuthorizeAttibute
{
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        Helper.Write("AuthorizationFilter", actionContext.RequestContext.Prioncipal);
        
        return base.IsAuthorized(actionContext);
    }
}

 

控制器增長受權特性

 

using System.Net.Http;


[TestAuthenticationFilter]
[TestAuthorizationFilter]
public class TestController : ApiController
{
    public IHttpActionResult Get()
    {
        Helper.Write("Controller", User);
        
        //獲取用戶也能夠這樣寫
        //Helper.Write("Controller",Request.GetRequestContext().Principal);
        return Ok();
    }
}

 

3.14 請求路由:localhsot:8000/api/test

顯示
------HttpModule--------
anonymous user

------Middleware--------
anonymous user

------AuthenticationFilter--------
anonymous user

------AuthorizationFilter--------
anonymous user

並報錯:Authorization has been denied for this request

可見,在請求尚未到達Controller以前,就開始報錯了。

因而,修改TestAuthorizationFilterAttrbute類以下:

public class TestAuthorizationFilterAttribute : AuthorizeAttibute
{
    protected override bool IsAuthorized(HttpActionContext actionContext)
    {
        Helper.Write("AuthorizationFilter", actionContext.RequestContext.Prioncipal);
        
        //return base.IsAuthorized(actionContext);
        return true;
    }
}

 

3.15 請求路由:localhsot:8000/api/test

顯示
------HttpModule--------
anonymous user

------Middleware--------
anonymous user

------AuthenticationFilter--------
anonymous user

------AuthorizationFilter--------
anonymous user

------Controller--------
anonymous user

可見,路由一路通過安全管道中的HttpModule, OWIN, AuthenticaitonFilter, AuthorizationFilter, Controller,依然沒有獲取到用戶信息?

3.16 用戶信息從哪裏注入呢?

接下來要修改TestMiddleware類

 

using Microsoft.Owin;
using System.Security.Principal;

namespace SecurityPopeline.Pipeline
{
    public class TestMiddleware
    {
        private Func<IDictionary<string, object>, Task> _next;
        public TestMiddleware(Func<IDictionary<string, object>, Task> next)
        {
            _next = next;
        }
        
        public async Task Invoke(IDictionary<string, object> env)
        {
            var context = new OwinContext(env);
            
            //authentication
            //new string[]數組存放用戶
            context.Request.User = new GenericPrincipal(new GenericIdentity("dom"),new string[]{});
            
            Helper.Write("Middleware", context.Request.User);
            await _next(env);
        }
    }
}

 

3.17 請求路由:localhsot:8000/api/test

顯示
------HttpModule--------
anonymous user

------Middleware--------
User: dom

------AuthenticationFilter--------
User: dom

------AuthorizationFilter--------
User: dom

------Controller--------
User: dom


總結:請求一路過來,會通過安全管道中的HttpModule, OWIN,AuthenticaitonFilter, AuthorizationFilter, Controller,最後到達Action, 而用戶信息能夠在OWIN中注入。

相關文章
相關標籤/搜索