.Net Core AOP之AuthorizeAttribute
一、简介
在.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、无权限页面