本篇体验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, IIS2、自宿主,WCF,.NET进程如果把OWIN考虑进去,那就是:1、IIS→ASP.NET+OWIN Bridge→ OWIN→Web API + OWIN Adapter2、Process/Host+OWIN Bridge→OWIN→Web API + OWIN Adapter一、了解管道中的各个组件
1.1 OWIN中间件
public class AuthenticationMiddleware{ private readonly Func, Task> _next; public AuthenticationMiddleware(Func , Task> next) { _next = next; } public async Task Invoke(IDictionary 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 TaskSendAsync(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.csconfig.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注册一下。
如果此时项目下有一个default.html页面的话,运行项目,展示default.html的时候,控制台打印出如下信息:
-----HttpModule-------anonymouse显然,请求过来,自定义的Http Module起了作用,但目前还不能从IPrincipal中拿到User信息。2.2 安装ASP.NET Web API 22.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.SystemWeb2.5 安装Microsoft. ASP.NET Web API 2.1 OWIN2.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, Task> _next; public TestMiddleware(Func , Task> next) { _next = next; } public async Task Invoke(IDictionary 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, Task> _next; public TestMiddleware(Func , Task> next) { _next = next; } public async Task Invoke(IDictionary 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中注入。