很多時候其實我們並不需要asp.net core自帶的那麼複雜的使用者係統,基於角色,各種概念,還得用EF Core,而且在web應用程式中都是把資訊儲存到cookie#中進行通訊(我不喜歡放cookie中,因為有次我在mac系統中的safari瀏覽器執行web應用程式時,碰到跨域cookie設不上,非要使用個很特殊的方法,記得是iframe,挺麻煩的,所以我還是喜歡放自訂header中), 用了以後感覺被微軟綁架了。不過這完全是個人喜好,大家完全可以照自己喜歡的來,我這裡提供了另一條路,大家可以多一種選擇。
我這邊是利用asp.net core的依賴注入,定義了一套屬於自己系統的使用者認證與授權,大家可以參考我這個來定義自己的,也不局限於用戶系統。
在我看來,Middleware與Filter都是asp.net core中的切面,我們可以把認證與授權放到這兩塊地方。我個人比較喜歡把認證放到Middleware,可以提早把那些不合法的攻擊攔截回來。
依賴注入有3種生命週期
1. 在同一個請求發起到結束。 (services.AddScoped)
2. 每次注入的時候都是新建。 (services.AddTransient)
3. 單例,應用程式開始到應用程式結束。 (services.AddSingleton)
我的自訂使用者類別採用的是services.AddScoped。
1 // 用户类,随便写的2 public class MyUser3 {4 public string Token { get; set; }5 public string UserName { get; set; }6 }
Startup.cs中的ConfigureServices函數:
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引用到的是同一個物件。
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 else23 {24 // 设置用户的token25 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 AuthenticationMiddlewareExtensions39 {40 public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder)41 {42 return builder.UseMiddleware<AuthenticationMiddleware>();43 }44 }
我發現如果要把介面/類別以Scoped方式註入到Middleware中,就需要把要注入的類別/介面放到Invoke函數的參數#中,而不是Middleware的建構子中,我猜這也是為什麼Middleware沒有繼承基類或接口,在基類或接口中定義好Invoke的原因,如果它在基類或接口中定義好Invoke,勢必這個Invoke的參數要固定死,就不好依賴注入了。
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 // 除了特殊路径外,都需要加上认证的Middleware11 app.MapWhen(context => !context.Request.Path.StartsWithSegments("/api/token")12 && !context.Request.Path.StartsWithSegments("/swagger"), x =>13 {14 // 使用自定义的Middleware15 x.UseAuthenticationMiddleware();16 // 使用通用的Middleware17 ConfigCommonMiddleware(x);18 });19 // 使用通用的Middleware20 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 // 配置通用的Middleware33 private void ConfigCommonMiddleware(IApplicationBuilder app)34 {35 // cors36 app.UseCors("AllowAll");37 38 app.UseExceptionMiddleware();39 // app.UseLogRequestMiddleware();40 app.UseMvc();41 }
像是取得token啊,檢視api文檔啊就不需要認證了。
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來取得到對應的使用者資訊。
1 [TypeFilter(typeof(NeedAuthAttribute), Arguments = new object[]{ "bbb" }, Order = 1)]2 public class ValuesController : Controller
這裡使用了TypeFilter,以載入使用了依賴注入的Filter, 並可以設定參數,跟Filter的順序。
預設Filter的順序是 全域設定->Controller->Action, Order預設都是0,我們可以透過設定Order來改變這個順序。
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。
以上是asp.net core使用DI實作自訂使用者係統的詳細內容。更多資訊請關注PHP中文網其他相關文章!