本篇体验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的宿主有两种方式:

1、Web宿主,ASP.NET, IIS
2、自宿主,WCF,.NET进程

如果把OWIN考虑进去,那就是:
1、IIS→ASP.NET+OWIN Bridge→ OWIN→Web API + OWIN Adapter
2、Process/Host+OWIN Bridge→OWIN→Web API + OWIN Adapter

一、了解管道中的各个组件

1.1 OWIN中间件

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开发出来的验证组件,大致是:

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

当然过滤器也可以放在控制器和方法层面:

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

1.5 Authorization Filter 授权过滤器

[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。

二、通过例子来体验安全管道

2.1 自定义HttpModule

首先,请求过来,肯定要通过HttpModule。我们需要自定义一个HttpModule,通过一个版主方法把当前的用户信息打印出来。

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注册一下。

<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中注入。

ASP.NET Web API的安全管道的更多相关文章

  1. ASP.NET Web API标准的“管道式”设计

    ASP.NET Web API的核心框架是一个消息处理管道,这个管道是一组HttpMessageHandler的有序组合.这是一个双工管道,请求消息从一端流入并依次经过所有HttpMessageHan ...

  2. ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇]

    ASP.NET Web API的消息处理管道: Self Host下的消息处理管道[上篇] ASP.NET Web API服务端框架核心是一个独立于具体寄宿环境的消息处理管道,它不关心请求消息来源于何 ...

  3. ASP.NET Web API的消息处理管道: HttpRoutingDispatcher

    ASP.NET Web API的消息处理管道: HttpRoutingDispatcher 认情况下,作为消息处理管道“龙头”的HttpServer的Dispatcher属性返回一个HttpRouti ...

  4. ASP.NET Web API的消息处理管道:"龙头"HttpServer

    ASP.NET Web API的消息处理管道:"龙头"HttpServer 一般来说,对于构成ASP.NET Web API消息处理管道的所有HttpMessageHandler来 ...

  5. ASP.NET Web API 2 消息处理管道

    Ø  前言 ASP.NET 的应用程序都会有自己的消息处理管道和生命周期,比如:ASP.NET Web 应用程序(Web Form).ASP.NET MVC,还有本文将讨论的 ASP.NET Web ...

  6. ASP.NET Web API标准的“管道式”设计

    详见:http://www.cnblogs.com/artech/p/asp-net-web-api-pipeline.html http://www.codeproject.com/Articles ...

  7. ASP.NET Web API WebHost宿主环境中管道、路由

    ASP.NET Web API WebHost宿主环境中管道.路由 前言 上篇中说到ASP.NET Web API框架在SelfHost环境中管道.路由的一个形态,本篇就来说明一下在WebHost环境 ...

  8. ASP.NET Web API Selfhost宿主环境中管道、路由

    ASP.NET Web API Selfhost宿主环境中管道.路由 前言 前面的几个篇幅对Web API中的路由和管道进行了简单的介绍并没有详细的去说明一些什么,然而ASP.NET Web API这 ...

  9. ASP.NET Web API 管道模型

    ASP.NET Web API 管道模型 前言 ASP.NET Web API是一个独立的框架,也有着自己的一套消息处理管道,不管是在WebHost宿主环境还是在SelfHost宿主环境请求和响应都是 ...

随机推荐

  1. socket方法

    // 创建一个Socket实例var socket = new WebSocket('ws://192.168.2.72:8430'); // 打开Socket socket.onopen = fun ...

  2. 代码中修改组件的margin属性

    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.WRAP_ ...

  3. [转载~笔记]CentOS单独编译安装PHP gd库扩展

    http://www.nowamagic.net/librarys/veda/detail/2610 安装gd前置库: freetype, jpegsrc, libpng 1. freetypewge ...

  4. WebService 调用

    一.WebService调用 1.webservice支持四种调用方式:SOAP 1.1,SOAP 1.2,GET,POST.           2.如果要webservice支持GET,POST调 ...

  5. 【Java学习笔记】<集合框架>定义功能去除ArrayList中的重复元素

    import java.util.ArrayList; import java.util.Iterator; import cn.itcast.p1.bean.Person; public class ...

  6. PopupWindow

    以前对于提示类型UI用到了PopupWindow 通过构造函数或者setContentView(View contentView)可以设置其显示内容: 显示时showAtLocation(View p ...

  7. git flow的安装和使用

    确保安装了git 1.windows系统下安装 进入cmd clone github上的gitflow到一个文件夹下 我这里clone到 c:\gitflow git clone git://gith ...

  8. Linux中exec命令相关

    Linux中exec命令相关 exec和source都属于bash内部命令(builtins commands),在bash下输入man exec或man source可以查看所有的内部命令信息. b ...

  9. 百度上传工具webuploader,图片上传附加参数

    项目中需要上传视频,图片等资源.最先做的是上传图片,开始在网上找了一款野鸡插件,可以实现图片上传预览(无需传到后台).但是最近这个插件出了莫名的问题,不易修复,一怒之下,还是决定找个大点的,靠谱的插件 ...

  10. getUserMedia

    index.ejs getUserMedia()方法有三个参数: 1.约束对象 2.成功回调函数,传入参数:LocalMediaStream 3.失败回调函数,传入参数:error object &l ...