本篇体验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. 洛谷P1738 洛谷的文件夹

    原题目:点我 题目是一个略水的题,我机制地用面向对象做了...所以代码量急剧加大,100行233 模拟即可,字符串处理麻烦点.如果没有找到子文件夹就新建文件夹,如果有就进入该文件夹. 提示:高能,指针 ...

  2. php中or的含义

    or其实是Php中的短路或 经常看到这样的语句: $file = fopen($filename, r) or die("抱歉,无法打开: $filename"); or在这里是这 ...

  3. ASP.NET访问网络映射盘&实现文件上传读取功能

    最近在改Web的时候,遇到一个问题,要跨机器访问共享文件夹,以实现文件正常上传下载功能. 要实现该功能,可以采用HTTP的方式,也可以使用网络映射磁盘的方式,今天主要给大家分享一下使用网络映射磁盘的方 ...

  4. SQL删除重复数据

    --首先将不是重复的数据提取出来,保存到一个临时表中 select distinct * into #temp from JX_Score --然后删除原来的表 delete from JX_Scor ...

  5. 【JSP】自定义标签开发入门

    JSP 自定义标签 自定义标签是用户定义的JSP语言元素.当JSP页面包含一个自定义标签时将被转化为servlet,标签转化为对被 称为tag handler的对象的操作,即当servlet执行时We ...

  6. js 图片预览

    图片预览 $('#pac_recipe').change(function() { var imgsrc = ''; ]) { //chrome firefox imgsrc = window.URL ...

  7. Oracle数据库11g基于rehl6.5的配置与安装

    REDHAT6.5安装oracle11.2.4 ORACLE11G R2官档网址: http://docs.oracle.com/cd/E11882_01/install.112/e24326/toc ...

  8. ORACLE升级的一些事

    一.SQL> @?/rdbms/admin/catupgrd.sql 说明:? 代表 ORACLE_HOME,在Linux中可能以 $ORACLE_HOME表示. @ 表示执行脚本 参考: ht ...

  9. C++混合编程之idlcpp教程Lua篇(3)

    上一篇 C++混合编程之idlcpp教程Lua篇(2) 是一个 hello world 的例子,仅仅涉及了静态函数的调用.这一篇会有新的内容. 与LuaTutorial0相似,工程LuaTutoria ...

  10. Dynamic CRM 2013学习笔记(十九)自定义审批流1 - 效果演示

    CRM的项目,审批流是一个必须品.为了更方便灵活地使用.配置审批流,我们自定义了一整套审批流.首先来看下它的效果: 1. 审批模板 这是一个最简单的审批流,首先指定审批实体,及相关字段,再配置流程节点 ...