asp.net core webapi 下面,想做一个过滤权限的Filter,配合token,对api做一个较为细粒度的权限控制,
该filter (PermissionFilter) 的作用是用户LoginUser.Permissions 列表中有 Key指定的权限才可以访问,没有则返回403 错误码。
1. 先上封装后的使用效果- [Permission(Key = "/User/AddUser")]
- [HttpPost]
- public Result AddUser([FromBody] SaUser user)
- {
- //Do sth.
- throw new NotImplementedException();
- }<br>
复制代码- [Authorize]
- [HttpPost]
- public Result<UserInfoDto> GetUserInfo()
- {
- //Do sth.
- }
复制代码 说明:要求登录即可,不要求特定权限的,可以使用【Authroize】 attribute 标记,
要求 特定权限 如 "/User/AddUser" 的 ,使用 【Permission】特性标记,使用Key指定需要的权限。 没有登录的返回401, 没有权限的返回403.
2. 实现。主要类及接口说明:
LoginUser : 登录用户,包含用户基础信息,权限等。可以继承此类封装更多信息。- namespace WebUtils
- {
- public class LoginUser
- {
- public string EnterpriseId { get; set; }
- public string UserName { get; set;}
- public string Token { get; set; }
- public DateTime LoginTime { get; set;}
- /// <summary>
- /// 可用权限
- /// </summary>
- public HashSet<string> Permissions { get; set;}
- }
- }
复制代码
ITokenHelper : 管理用户登录后的token,并根据token 获取登录用户信息。TUser 是LoginUser 的子类。 - namespace WebUtils
- {
- public interface ITokenHelper<TUser> where TUser :LoginUser
- {
- public void AddToken(string token, TUser user);
- public void RemoveToken(string token);
- public TUser GetLoginUser (string token);
- }
- }
复制代码
TokenHelper 是 ITokenHelper 的默认实现,LoginUser 和Token 存内存中,进程重启会丢失。实际应用可以封装自己的实现,把信息持久化到数据库或者Redis 中。
- namespace WebUtils
- {
- public class TokenHelper : ITokenHelper<LoginUser>
- {
-
- private Dictionary<string, LoginUser> UserDic = new Dictionary<string, LoginUser>();
-
- public void AddToken(string token, LoginUser au)
- {
- UserDic.Add(token, au);
- }
-
- public LoginUser GetLoginUser(string token)
- {
- if (UserDic.ContainsKey(token))
- {
- return UserDic[token];
- }
- return null;
- }
- public void RemoveToken(string token)
- {
- if (UserDic.ContainsKey(token))
- {
- UserDic.Remove(token);
- }
- }
- }
- }
复制代码 View Code
PermissionAuthenticationHandler:检查请求是否携带token,并检查TokenHelper 中是否包含此token.
- using Microsoft.AspNetCore.Authentication;
- using Microsoft.AspNetCore.Http;
- using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Options;
- using Microsoft.Extensions.Primitives;
- using System;
- using System.Net;
- using System.Security.Claims;
- using System.Text.Encodings.Web;
- using System.Threading.Tasks;
- using WebUtils;
- namespace WebUtils
- {
- public class PermissionAuthenticationHandler : AuthenticationHandler
- {
- private ITokenHelper<LoginUser> _tokenHelper;
- public PermissionAuthenticationHandler(ITokenHelper<LoginUser> tokenHelper, IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
- : base(options, logger, encoder, clock)
- {
- this._tokenHelper = tokenHelper;
- }
- public static string CustomerSchemeName = "Permission";
- protected override Task HandleAuthenticateAsync()
- {
- AuthenticateResult result;
- Context.Request.Headers.TryGetValue("Authorization", out StringValues values);
- string token = values.ToString();
- if (!string.IsNullOrWhiteSpace(token))
- {
- var loginInfo = _tokenHelper.GetLoginUser(token);
- if (loginInfo == null)
- result = AuthenticateResult.Fail("未登陆");
- else
- {
- var claimsIdentity = new ClaimsIdentity(new Claim[]
- {
- new Claim(ClaimTypes.Name, loginInfo.UserName),
- new Claim(ClaimHelper.EnterpriseId,loginInfo.EnterpriseId),
- new Claim(ClaimHelper.Token, loginInfo.Token)
- }, CustomerSchemeName);
- var principal = new ClaimsPrincipal(claimsIdentity);
- AuthenticationTicket ticket = new AuthenticationTicket(principal, Scheme.Name);
- result = AuthenticateResult.Success(ticket);
- }
- }
- else
- {
- result = AuthenticateResult.Fail("未登陆");
- }
- return Task.FromResult(result);
- }
- }
- }
复制代码 View Code
PermissionAttribute: 继承自 Attribute,IFilterFactory ,返回真正的IAuthorizationFilter实例。
DonotUsePermissionFilterAttribute 继承自 Attribute, IAuthorizationFilter 检查是否拥有指定的权限。
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.AspNetCore.Mvc.Filters;
- using Microsoft.Extensions.DependencyInjection;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace WebUtils
- {
- [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)]
- public class PermissionAttribute : Attribute,IFilterFactory
- {
- public string Key { get; set; }
- public bool IsReusable => false;
- public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
- {
- var instance= serviceProvider.GetService<DonotUsePermissionFilterAttribute>();
- instance.Key = this.Key;
- return instance;
- }
- }
- /// <summary>
- /// 防止用户直接调用,起名DonotUse,
- /// </summary>
- public class DonotUsePermissionFilterAttribute : Attribute, IAuthorizationFilter
- {
- private ITokenHelper<LoginUser> _tokenHelper;
- public DonotUsePermissionFilterAttribute(ITokenHelper<LoginUser> tokenHelper)
- {
- this._tokenHelper = tokenHelper;
- }
- public string Key { get; set; }
-
- public void OnAuthorization(AuthorizationFilterContext context)
- {
- var token = context.HttpContext.User?.GetValue(ClaimHelper.Token);
- if (token == null)
- {
- context.Result = new ObjectResult("用户未登录") { StatusCode = 401 };
- return;
- }
- var user = _tokenHelper.GetLoginUser(token);
- if (user == null)
- {
- context.Result = new ObjectResult("用户token 已失效") { StatusCode = 401 };
- return;
- }
- if (!user.Permissions.Contains(Key))
- {
- context.Result = new ObjectResult("鉴权失败,请联系管理员授权!") { StatusCode = 403 };
- return;
- }
- }
- }
- }
复制代码 View Code
PermissionMiddleWare 把相关实例和PermissionAuthenticationHandler添加到Service 中。
- using Microsoft.Extensions.DependencyInjection;
- namespace WebUtils
- {
- public static class PermissionMiddleWare
- {
- /// <summary>
- /// 基于token和permission 的权限认证中间件
- /// </summary>
- /// <param name="services"></param>
- /// <param name="TokenHelperType"></param>
- /// <returns></returns>
- public static IServiceCollection AddPermission(this IServiceCollection services,Type TokenHelperType)
- {
- services.AddSingleton(typeof(ITokenHelper<LoginUser>), TokenHelperType);
- services.AddTransient(typeof(PermissionAttribute));
- services.AddTransient(typeof(DonotUsePermissionFilterAttribute));
- services.AddAuthentication(o =>
- {
- o.DefaultAuthenticateScheme = PermissionAuthenticationHandler.CustomerSchemeName;
- o.DefaultChallengeScheme = PermissionAuthenticationHandler.CustomerSchemeName;
- o.AddScheme<PermissionAuthenticationHandler>(PermissionAuthenticationHandler.CustomerSchemeName, PermissionAuthenticationHandler.CustomerSchemeName);
- });
- return services;
- }
- }
- }
复制代码 View Code 3. 在program.cs 中调用
在原来添加AddAuthorization 的地方换成下面这句- builder.Services.AddPermission(typeof(TokenHelper));
复制代码 别忘了后面use- app.UseAuthentication();
- app.UseAuthorization();
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |