.Net Core AOP之AuthorizeAttribute

SportSky發表於2022-02-27

一、簡介

在.net core 中Filter分為以下六大類:

1、AuthorizeAttribute(許可權驗證)

2、IResourceFilter(資源快取)

3、IActionFilter(執行方法前後的記錄)

4、IResultFilter(結果生成前後擴充套件)

5、IAlwaysRun(響應結果的補充)

6、IExceptionFilter(異常處理)

二、AuthorizeAttribute(許可權驗證)

認證授權分為三種,如下:

1、基於角色授權

1.1、配置Startup.cs 類,使用Cookie及角色授權方式訪問 —— 修改 ConfigureServices 與 Configure方法

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            { 
                // ************1、新增鑑權和授權邏輯**************************
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
                {
                    options.LoginPath = new PathString("/Login/LoginView"); // 登入地址
                    options.AccessDeniedPath = new PathString("/Login/AccessDenied"); // 無許可權訪問需要跳轉的頁面地址
                    options.LogoutPath = new PathString("/Login/LoginOff"); // 登出地址
                    options.ExpireTimeSpan = TimeSpan.FromMinutes(1); // cookie有效時間(這裡設定的1分鐘有效時間)
                    options.Cookie = new CookieBuilder
                    {
                        // cookie名稱,Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")取得是當前環境變數的名稱,使用者可自定義
                        Name = $"WebUI_{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}" 
                    };
                });
                services.AddAuthorization();
                // **********************************************************
            }
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseRouting();

            // ***************2、鑑權*******************
            app.UseAuthentication();
            // 授權
            app.UseAuthorization();
            
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Login}/{action=LoginView}/{id?}");
            });
        }

 

1.2、使用授權的時候在Action或controller上打上Authorize特性並賦值Role屬性,表示該方法只能由Admin的角色訪問

public class LoginController : Controller
    {
        // 登入頁面
        public IActionResult LoginView()
        {
            return View();
        }

        /// <summary>
        /// 登入方法
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        public async Task<IActionResult> Login()
        {

            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name,"xiaohemiao"),
                new Claim(ClaimTypes.Role,"Admin")
            };
            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme
                , new ClaimsPrincipal(claimsIdentity)
                , new AuthenticationProperties()
                {
                    ExpiresUtc = DateTime.UtcNow.AddMinutes(1)
                }); 
           
            return Redirect("/Login/Index");
        }

        /// <summary>
        /// 登入成功之後跳轉的頁面
        /// </summary>
        /// <returns></returns>
        [Authorize(Roles = "Admin")]
        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// 登出
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> LoginOff()
        {
            await HttpContext.SignOutAsync();
            return Redirect("/Login/LoginView");
        }

        /// <summary>
        /// 無許可權頁面
        /// </summary>
        /// <returns></returns>
        public IActionResult AccessDenied()
        {
            return View();
        }

        
    }

 

2、基於宣告授權

修改基於標題1的相關程式碼

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            { 
                // ************1、新增鑑權和授權邏輯**************************
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
                {
                    options.LoginPath = new PathString("/Login/LoginView"); // 登入地址
                    options.AccessDeniedPath = new PathString("/Login/AccessDenied"); // 無許可權訪問需要跳轉的頁面地址
                    options.LogoutPath = new PathString("/Login/LoginOff"); // 登出地址
                    options.ExpireTimeSpan = TimeSpan.FromMinutes(1); // cookie有效時間(這裡設定的1分鐘有效時間)
                    options.Cookie = new CookieBuilder
                    {
                        // cookie名稱,Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")取得是當前環境變數的名稱,使用者可自定義
                        Name = $"WebUI_{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}" 
                    };
                });
                services.AddAuthorization(options => {
                    // 當角色是Admin和SuperAdministrator才可以訪問
                    options.AddPolicy("AdministratorOnly", policy => policy.RequireClaim(ClaimTypes.Role, "Admin", "SuperAdministrator"));
                });
                // **********************************************************
            }
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseRouting();

            // ***************2、鑑權*******************
            app.UseAuthentication();
            // 授權
            app.UseAuthorization();
            
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Login}/{action=LoginView}/{id?}");
            });
        }

 

public class LoginController : Controller
    {
        // 登入頁面
        public IActionResult LoginView()
        {
            return View();
        }

        /// <summary>
        /// 登入方法
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        public async Task<IActionResult> Login()
        {

            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name,"xiaohemiao"),
                new Claim(ClaimTypes.Role,"Admin")
            };
            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme
                , new ClaimsPrincipal(claimsIdentity)
                , new AuthenticationProperties()
                {
                    ExpiresUtc = DateTime.UtcNow.AddMinutes(1)
                }); 
           
            return Redirect("/Login/Index");
        }

        /// <summary>
        /// 登入成功之後跳轉的頁面
        /// </summary>
        /// <returns></returns>
        [Authorize(Policy = "AdministratorOnly")]
        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// 登出
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> LoginOff()
        {
            await HttpContext.SignOutAsync();
            return Redirect("/Login/LoginView");
        }

        /// <summary>
        /// 無許可權頁面
        /// </summary>
        /// <returns></returns>
        public IActionResult AccessDenied()
        {
            return View();
        }

        
    }

 

3、自定義策略授權

3.1、定義許可權策略

 

public class PermissionRequirement: IAuthorizationRequirement
    {

    }

 

3.2、再定義個策略處理類

public class RoleAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
    {
        private readonly ILogger<RoleAuthorizationHandler> _logger;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public RoleAuthorizationHandler(ILogger<RoleAuthorizationHandler> logger, IHttpContextAccessor httpContextAccessor)
        {
            _logger = logger;
            this._httpContextAccessor = httpContextAccessor;
        }
        public override Task HandleAsync(AuthorizationHandlerContext context)
        {
            var mvcContext = _httpContextAccessor.HttpContext;
            var user = context.User.FindFirst(ClaimTypes.Role)?.Value;
           
            if (mvcContext.User.Identity.IsAuthenticated)
            {
                var routes = mvcContext.GetRouteData();
                var controller = routes.Values["controller"]?.ToString()?.ToLower();
                var action = routes.Values["action"]?.ToString()?.ToLower();

                var activeTime = mvcContext.User.FindFirst(ClaimTypes.Expired);
                // 是否登入超時
                if (activeTime == null || Convert.ToDateTime(activeTime.Value) < DateTime.Now)
                {
                    // 登入超時自動跳轉到登入頁面
                    mvcContext.Response.Redirect("/Login/LoginView");
                    context.Succeed(context.Requirements.FirstOrDefault());
                    return Task.CompletedTask;
                }

                var hasRole = mvcContext.User.HasClaim(c => c.Type == ClaimTypes.Role);
                if (!hasRole)
                {
                    //使用者未在系統新增,即使登入成功,也要提示沒有許可權
                    context.Fail();
                    return Task.CompletedTask;
                }

                var menuPaths = AuthorizationMenuPath(user);
                string route = $"/{controller}";
               
                var actionRoute = $"/{controller}/{(routes.Values["action"] ?? "Index")}".ToLower();
                if (menuPaths.Any(m => m.ToLower().Contains($"/{controller}/")) || menuPaths.Any(m => m.ToLower() == route) || menuPaths.Any(m => m.ToLower() == actionRoute))
                    context.Succeed(context.Requirements.FirstOrDefault());
                else context.Fail();//會預設跳轉 accessdenied檢視
                
            }
            else
                context.Fail();

            return Task.CompletedTask;
        }
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
        {
            throw new NotImplementedException();
        }


        /// <summary>
        /// 許可權動態快取類 臨時替代資料庫
        /// </summary>
        /// <param name="roleName">角色名稱</param>
        /// <returns></returns>
        private List<string> AuthorizationMenuPath(string roleName)
        {
            switch (roleName)
            {
                case "Admin":
                    return new List<string>() { "/Login/Index" };
                default:
                    return new List<string>() { "/Login/Index" };
            }
            
        }
    }

 

3.3、修改 ConfigureServices 與 Configure方法

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            {

                // ************3、注入自定義策略**************************
                services.AddSingleton<IAuthorizationHandler, RoleAuthorizationHandler>();
                // ************1、新增鑑權和授權邏輯**************************
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
                {
                    options.LoginPath = new PathString("/Login/LoginView"); // 登入地址
                    options.AccessDeniedPath = new PathString("/Login/AccessDenied"); // 無許可權訪問需要跳轉的頁面地址
                    options.LogoutPath = new PathString("/Login/LoginOff"); // 登出地址
                    options.ExpireTimeSpan = TimeSpan.FromMinutes(1); // cookie有效時間(這裡設定的1分鐘有效時間)
                    options.Cookie = new CookieBuilder
                    {
                        // cookie名稱,Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")取得是當前環境變數的名稱,使用者可自定義
                        Name = $"WebUI_{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}" 
                    };
                });
                services.AddAuthorization(options =>
                {
                    options.AddPolicy("RolePolicy", policy =>policy.Requirements.Add(new PermissionRequirement()));
                   
                });

                services.AddHttpContextAccessor();
                // **********************************************************
            }
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();

            app.UseRouting();

            // ***************2、鑑權*******************
            app.UseAuthentication();
            // 授權
            app.UseAuthorization();
            
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Login}/{action=LoginView}/{id?}");
            });
        }

3.4 、controller程式碼邏輯

public class LoginController : Controller
    {
        // 登入頁面
        public IActionResult LoginView()
        {
            return View();
        }

        /// <summary>
        /// 登入方法
        /// </summary>
        /// <returns></returns>
        [AllowAnonymous]
        public async Task<IActionResult> Login()
        {

            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name,"xiaohemiao"),
                new Claim(ClaimTypes.Role,"Admin"),
                new Claim(ClaimTypes.Expired,DateTime.Now.AddMinutes(1).ToString("yyyy-MM-dd HH:mm:ss"))
                
            };
            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme
                , new ClaimsPrincipal(claimsIdentity)
                , new AuthenticationProperties()
                {
                    
                }); 
           
            return Redirect("/Login/Index");
        }

        /// <summary>
        /// 登入成功之後跳轉的頁面
        /// </summary>
        /// <returns></returns>
        [Authorize(Policy = "RolePolicy")]
        public IActionResult Index()
        {
            return View();
        }

        /// <summary>
        /// 登出
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> LoginOff()
        {
            await HttpContext.SignOutAsync();
            return Redirect("/Login/LoginView");
        }

        /// <summary>
        /// 無許可權頁面
        /// </summary>
        /// <returns></returns>
        public IActionResult AccessDenied()
        {
            return View();
        }

        
    }

 

三、簡單頁面呈現效果

1、登入頁面

 

 

2、登入成功跳轉的頁面

 

 

3、無許可權頁面

 

相關文章