ASP. NET Web API Security pipeline, asp. netapi
This article describes the Security pipelines of ASP. NET Web APIs. Here, the security pipeline refers to various components or processes experienced in the request and response process, such as IIS, HttpModule, OWIN, WebAPI, and so on. This pipeline is divided into two phases: one is the verification phase and the other is the authorization phase.
In ASP. NET Web API v1, the security pipeline is roughly like this:
→ Authentication: request to HttpModule in IIS
→ Authenticatin, the request comes to the HttpMessageHandler of the API
→ Authorization: Request comes to Authorization Filter
→ Authorization: request to Controller
When ASP. NET Web API comes to v2, the security pipeline is roughly:
→ Request to the OWIN component in the Host
→ The request comes to MessageHandler, global or according to each request
→ Request to Authentication Filter
→ Request to Authorization Filter
It can be seen that the OWIN component is added, and the OWIN component is open-source. On this basis, Microsoft has developed the Katana verification middleware.
We know that there are two ways to host ASP. NET Web APIs:
1. Web host, ASP. NET, IIS
2. Self-host, WCF,. NET Process
If you consider OWIN, it is:
1. IIS → ASP. NET + OWIN Bridge → OWIN → Web API + OWIN Adapter
2. Process/Host + OWIN Bridge → OWIN → Web API + OWIN Adapter
1. Understand the various components in the pipeline
1.1 OWIN Middleware
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) {// check the env set to verify env ["server. user "] = CreatePrincipal (); // set principal; await _ next (env );}}
The general working principle of OWIN middleware is: the Header, Body, route, and other information in the request is placed in the IDictionary <string, object> dictionary set, and the Invoke method is provided, place the obtained user information in env ["server. user "], and calls an action to process IDictionary <string, object> set.
1.2 Katana Authentication Middleware
This is a verification component developed by Microsoft Based on OWIN, which is roughly:
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 }) }}
As shown above, you can select the authentication method for the OWIN component.
1.3 Message Handler
Implemented globally or on a request. Roughly:
Public class MyHandler: DelegatingHandler {protected async override Task <HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken) {// check request var response = await base. sendAsync (request, cancellationToken); // check the response return response ;}}
Message Handler does not exist after ASP. net web api 2.
1.4 Authentication Filter
It can be configured globally:
WebApiConfig. cs
Config. Filters. Add (new HostAuthenticationFilter ("Bearer "));
Of course, the filter can also be placed on the controller and method layer:
[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){}}
If the authorization fails, error 401 is returned.
1.6 obtain the user's Identity
Obtain the User Identity through the User attribute of ApiController. Note that the User attribute value may be null.
2. Use examples to experience security Pipelines
2.1 custom HttpModule
First, the request must pass HttpModule. We need to customize an HttpModule to print the current user information through a moderator method.
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"); } }}
It can be seen that HttpContext. Current is of the IPrincipal type.
This is a Web project. You need to register the HTTP module.
<configuration> <system.webServer> <modules> <add name="DemoModule" type="SecurityPipeline.HttpModule"/> </modules> </system.webServer></configuration>
If there is a default.html page under the example project, run the project and display default.html, the console prints the following information:
----- HttpModule -------
Anonymouse
Apparently, the custom Http Module works for the request, but currently the User information cannot be obtained from IPrincipal.
2.2 install ASP. NET Web API 2
2.3 create a controller
Using System. net. http; public class TestController: ApiController {public IHttpActionResult Get () {Helper. write ("Controller", User); // you can also get the User to Write // Helper. write ("Controller", Request. getRequestContext (). principal); return OK ();}}
Above, the IPincipal type is obtained through the User attribute of ApiController.
2.4 install Microsoft. Owin. Host. SystemWeb
2.5 install Microsoft. ASP. NET Web API 2.1 OWIN
2.6 create a Startup class
using OWin;using System.Web.Http;namespace SecurityPopeline{ public class Startup { public void Configuraiton() { var configuration = new HttpConfiguration(); configuration.Routes.MapHttpRoute("default", "api/{controller}"); } }}
Here, assign the HttpConfiguraton instance of the web api to the UseWebApi method of IAppBuilder of the owin class.
2.7 request route: localhsot: 8000/api/test
Display
------ HttpModule --------
Anonymous user
------ Controller --------
Anonymous user
It can be seen that the request passes through the HttpModule and Controller in the pipeline, but the user information is still not obtained.
2.8 create the TestMiddleware class
After entering the HttpModule, and before entering the Controller, this is the place where the OWIN component will survive.
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 add TestMiddleware to the Startup class
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 request route: localhsot: 8000/api/test
Display
------ HttpModule --------
Anonymous user
------ Middleware --------
Anonymous user
------ Controller ------
Anonymous user
It can be seen that the request passes through the HttoModule and OWIN in the pipeline all the way, and finally arrives at the Controller, but still does not get the user information.
3.11 add TestAuthenticationFilterAttribute class
There are also verified interfaces between OWIN and Controller, which is also an important part of the security pipeline.
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; } } }}
Filter feature added to Controller
Using System. net. http; [TestAuthenticationFilter] public class TestController: ApiController {public IHttpActionResult Get () {Helper. write ("Controller", User); // you can also get the User to Write // Helper. write ("Controller", Request. getRequestContext (). principal); return OK ();}}
3.12 request route: localhsot: 8000/api/test
Display
------ HttpModule --------
Anonymous user
------ Middleware --------
Anonymous user
------ AuthenticationFilter --------
Anonymous user
------ Controller ------
Anonymous user
It can be seen that the user information is still not obtained for the HttpModule, OWIN, and authentication in the Request Path Security pipeline.
3.13 added the TestAuthorizationFilterAttrbute class
After verification and before entering the Controller or Action, an important member of the security pipeline is the authorization feature.
public class TestAuthorizationFilterAttribute : AuthorizeAttibute{ protected override bool IsAuthorized(HttpActionContext actionContext) { Helper.Write("AuthorizationFilter", actionContext.RequestContext.Prioncipal); return base.IsAuthorized(actionContext); }}
Added the authorization feature to the Controller.
Using System. net. http; [TestAuthenticationFilter] [TestAuthorizationFilter] public class TestController: ApiController {public IHttpActionResult Get () {Helper. write ("Controller", User); // you can also get the User to Write // Helper. write ("Controller", Request. getRequestContext (). principal); return OK ();}}
3.14 request route: localhsot: 8000/api/test
Display
------ HttpModule --------
Anonymous user
------ Middleware --------
Anonymous user
------ AuthenticationFilter --------
Anonymous user
------ AuthorizationFilter --------
Anonymous user
Error: Authorization has been denied for this request
It can be seen that an error is reported before the request reaches the Controller.
Therefore, modify the TestAuthorizationFilterAttrbute class as follows:
public class TestAuthorizationFilterAttribute : AuthorizeAttibute{ protected override bool IsAuthorized(HttpActionContext actionContext) { Helper.Write("AuthorizationFilter", actionContext.RequestContext.Prioncipal); //return base.IsAuthorized(actionContext); return true; }}
3.15 request route: localhsot: 8000/api/test
Display
------ HttpModule --------
Anonymous user
------ Middleware --------
Anonymous user
------ AuthenticationFilter --------
Anonymous user
------ AuthorizationFilter --------
Anonymous user
------ Controller --------
Anonymous user
It can be seen that the route passes through the HttpModule, OWIN, AuthenticaitonFilter, AuthorizationFilter, and Controller in the security pipeline, and the user information is still not obtained?
3.16 where can I inject user information?
Next, modify the TestMiddleware class.
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 [] array stores the user context. request. user = new GenericPrincipal (new GenericIdentity ("dom"), new string [] {}); Helper. write ("Middleware", context. request. user); await _ next (env );}}}
3.17 request route: localhsot: 8000/api/test
Display
------ HttpModule --------
Anonymous user
------ Middleware --------
User: dom
------ AuthenticationFilter --------
User: dom
------ AuthorizationFilter --------
User: dom
------ Controller --------
User: dom
Summary: requests are sent all the way through the HttpModule, OWIN, AuthenticaitonFilter, AuthorizationFilter, and Controller in the security pipeline, and finally to the Action. User information can be injected into the OWIN.