asp.net core利用DI實現自定義用戶系統,脫離ControllerBase.User

前言

不少時候其實咱們並不須要asp.net core自帶的那麼複雜的用戶系統,基於角色,各類概念,還得用EF Core,並且在web應用中都是把信息存儲到cookie中進行通信(我不喜歡放cookie中,由於有次我在mac系統中的safari瀏覽器運行web應用時,碰到跨域cookie設不上,非要使用個很特殊的方法,記得是iframe,挺麻煩的,因此我仍是喜歡放自定義header中), 用了之後感受被微軟給綁架了。不過這徹底是我的喜愛,你們徹底能夠按本身喜歡的來,我這裏提供了另一條路,你們能夠多一種選擇。web

我這邊是利用asp.net core的依賴注入,定義了一套屬於本身系統的用戶認證與受權,你們能夠參考我這個來定義本身的,也不侷限於用戶系統。數據庫

面向切面編程(AOP)

在我看來,Middleware與Filter都是asp.net core中的切面,咱們能夠把認證與受權放到這兩塊地方。我我的比較喜歡把認證放到Middleware,能夠提前把那些不合法的攻擊攔截返回。編程

依賴注入(DI)

依賴注入有3種生命週期json

1. 在同一個請求發起到結束。(services.AddScoped)api

2. 每次注入的時候都是新建。(services.AddTransient)跨域

3. 單例,應用開始到應用結束。(services.AddSingleton)瀏覽器

個人自定義用戶類採用的是services.AddScoped。cookie

具體作法

1. 定義用戶類

1     // 用戶類,隨便寫的
2     public class MyUser
3     {
4         public string Token { get; set; }
5         public string UserName { get; set; }
6     }

2. 註冊用戶類

Startup.cs中的ConfigureServices函數:app

1         // This method gets called by the runtime. Use this method to add services to the container.
2         public void ConfigureServices(IServiceCollection services)
3         {
4             ...
5             // 註冊自定義用戶類
6             services.AddScoped(typeof(MyUser));
7             ...
8         }

自定義用戶類,是經過services.AddScoped方式進行註冊的,由於我但願它在同一個請求中,Middleware, filter, controller引用到的是同一個對象。cors

3. 注入到Middleware

 1     // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
 2     public class AuthenticationMiddleware
 3     {
 4         private readonly RequestDelegate _next;
 5         private IOptions<HeaderConfig> _optionsAccessor;
 6 
 7         public AuthenticationMiddleware(RequestDelegate next, IOptions<HeaderConfig> optionsAccessor)
 8         {
 9             _next = next;
10             _optionsAccessor = optionsAccessor;
11         }
12 
13         public async Task Invoke(HttpContext httpContext, MyUser user)
14         {
15             var token = httpContext.Request.Headers[_optionsAccessor.Value.AuthHeader].FirstOrDefault();
16             if (!IsValidate(token))
17             {
18                 httpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
19                 httpContext.Response.ContentType = "text/plain";
20                 await httpContext.Response.WriteAsync("UnAuthentication");
21             }
22             else
23             {
24                 // 設置用戶的token
25                 user.Token = token;
26                 await _next(httpContext);
27             }
28         }
29 
30         // 隨便寫的,你們能夠加入些加密,解密的來判斷合法性,你們自由發揮
31         private bool IsValidate(string token)
32         {
33             return !string.IsNullOrEmpty(token);
34         }
35     }
36 
37     // Extension method used to add the middleware to the HTTP request pipeline.
38     public static class AuthenticationMiddlewareExtensions
39     {
40         public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder)
41         {
42             return builder.UseMiddleware<AuthenticationMiddleware>();
43         }
44     }

我發現若是要把接口/類以Scoped方式注入到Middleware中,就須要把要注入的類/接口放到Invoke函數的參數中,而不是Middleware的構造函數中,我猜這也是爲何Middleware沒有繼承基類或者接口,在基類或者接口中定義好Invoke的緣由,若是它在基類或者接口中定義好Invoke,勢必這個Invoke的參數要固定死,就很差依賴注入了。

4. 配置某些路徑纔會使用該Middleware

 1         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
 2         public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
 3         {
 4             loggerFactory.AddConsole(Configuration.GetSection("Logging"));
 5             loggerFactory.AddDebug();
 6             // Set up nlog
 7             loggerFactory.AddNLog();
 8             app.AddNLogWeb();
 9 
10             // 除了特殊路徑外,都須要加上認證的Middleware
11             app.MapWhen(context => !context.Request.Path.StartsWithSegments("/api/token")
12                                  && !context.Request.Path.StartsWithSegments("/swagger"), x =>
13             {
14                 // 使用自定義的Middleware
15                 x.UseAuthenticationMiddleware();
16                 // 使用通用的Middleware
17                 ConfigCommonMiddleware(x);
18             });
19             // 使用通用的Middleware
20             ConfigCommonMiddleware(app);
21 
22             // Enable middleware to serve generated Swagger as a JSON endpoint.
23             app.UseSwagger();
24 
25             // Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
26             app.UseSwaggerUI(c =>
27             {
28                 c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
29             });
30         }
31 
32         // 配置通用的Middleware
33         private void ConfigCommonMiddleware(IApplicationBuilder app)
34         {
35             // cors
36             app.UseCors("AllowAll");
37 
38             app.UseExceptionMiddleware();
39             // app.UseLogRequestMiddleware();
40             app.UseMvc();
41         }

像獲取token啊,查看api文檔啊就不須要認證了。

5. 注入到Filter

 1     public class NeedAuthAttribute : ActionFilterAttribute
 2     {
 3         private string _name = string.Empty;
 4         private MyUser _user;
 5 
 6         public NeedAuthAttribute(MyUser user, string name = "")
 7         {
 8             _name = name;
 9             _user = user;
10         }
11 
12         public override void OnActionExecuting(ActionExecutingContext context)
13         {
14             this._user.UserName = "aaa";
15         }
16     }

這裏我建立的是個帶字符串參數的類,由於考慮到這個Filter有可能會被複用,好比限制某個接口只能被某種用戶訪問, 這個字符串即可以存某種用戶的標識。

Filter中還能夠注入數據庫訪問的類,這樣咱們即可以到數據庫中經過token來獲取到相應的用戶信息。

6. 使用Filter

1 [TypeFilter(typeof(NeedAuthAttribute), Arguments = new object[]{ "bbb" }, Order = 1)]
2 public class ValuesController : Controller

這裏使用了TypeFilter,以加載使用了依賴注入的Filter, 並能夠設置參數,跟Filter的順序。

默認Filter的順序是 全局設置->Controller->Action, Order默認都爲0,咱們能夠經過設置Order來改變這個順序。

7. 注入到Controller

 1     public class ValuesController : Controller
 2     {
 3         private MyUser _user;
 4 
 5         public ValuesController(MyUser user)
 6         {
 7             _user = user;
 8         }
 9         ...
10     }

注入到Controller的構造函數中,這樣咱們就能夠在Controller的Action中使用咱們自定義的用戶,就能知道到底當前是哪一個用戶在調用這個Action。

相關文章
相關標籤/搜索