• 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的宿主有两种方式:

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

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

  • 相关阅读:
    Mongodb地理位置索引
    PHP中目录解析函数
    PHP中的date函数中时区问题
    PHP 文件上传全攻略
    PHP上传图片重命名6种方案
    C#|executequery要求已打开且可用的connection,连接的当前状态为已关闭
    C#|只有 assignment、call、increment、decrement 和 new 对象表达式可用作语句
    Android报错|Android Call requires API level 19 (current min is 15)
    机器学习|用机器学习预测谁将夺得世界杯冠军(附代码)
    Python|词云wordcloud入门示例
  • 原文地址:https://www.cnblogs.com/darrenji/p/5290073.html
Copyright © 2020-2023  润新知