扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
ASP.NET Core中怎么使用自定义验证属性控制访问权限?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
创新互联建站是一家专业提供婺城企业网站建设,专注与成都网站建设、成都做网站、H5高端网站建设、小程序制作等业务。10年已为婺城众多企业、政府机构等服务。创新互联专业网站制作公司优惠进行中。一、public class Startup的配置:
//启用跨域访问(不同端口也是跨域) services.AddCors(options => { options.AddPolicy("AllowOriginOtherBis", builder => builder.WithOrigins("https://1.16.9.12:4432", "https://pc12.ato.biz:4432", "https://localhost:44384", "https://1.16.9.12:4432", "https://pc12.ato.biz:4432").AllowAnyMethod().AllowAnyHeader()); }); //启用自定义属性以便对控制器或Action进行[TerminalApp()]定义。 services.AddSingleton(); services.AddAuthorization(options => { options.AddPolicy("TerminalApp", policyBuilder => { policyBuilder.Requirements.Add(new TerminalAppAuthorizationRequirement()); }); });
二、public void Configure(IApplicationBuilder app, IHostingEnvironment env)中的配置:
app.UseHttpsRedirection(); //使用Https传输 app.UseCors("AllowOriginOtherBis"); //根据定义启用跨域设置
三、示例WebApi项目结构:
四、主要代码(我采用的从数据库进行验证):
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] internal class TerminalAppAttribute : AuthorizeAttribute { public string AppID { get; } ////// 指定客户端访问API /// /// public TerminalAppAttribute(string appID="") : base("TerminalApp") { AppID = appID; } }
public abstract class AttributeAuthorizationHandler: AuthorizationHandler where TRequirement : IAuthorizationRequirement where TAttribute : Attribute { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement) { var attributes = new List (); if ((context.Resource as AuthorizationFilterContext)?.ActionDescriptor is ControllerActionDescriptor action) { attributes.AddRange(GetAttributes(action.ControllerTypeInfo.UnderlyingSystemType)); attributes.AddRange(GetAttributes(action.MethodInfo)); } return HandleRequirementAsync(context, requirement, attributes); } protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable attributes); private static IEnumerable GetAttributes(MemberInfo memberInfo) { return memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast (); } } internal class TerminalAppAuthorizationHandler : AttributeAuthorizationHandler { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, IEnumerable attributes) { object errorMsg = string.Empty; //如果取不到身份验证信息,并且不允许匿名访问,则返回未验证403 if (context.Resource is AuthorizationFilterContext filterContext && filterContext.ActionDescriptor is ControllerActionDescriptor descriptor) { //先判断是否是匿名访问, if (descriptor != null) { var actionAttributes = descriptor.MethodInfo.GetCustomAttributes(inherit: true); bool isAnonymous = actionAttributes.Any(a => a is AllowAnonymousAttribute); //非匿名的方法,链接中添加accesstoken值 if (isAnonymous) { context.Succeed(requirement); return Task.CompletedTask; } else { //url获取access_token //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息 var httpContext = (context.Resource as AuthorizationFilterContext).HttpContext; //var questUrl = httpContext.Request.Path.Value.ToLower(); string requestAppID = httpContext.Request.Headers["appid"]; string requestAccessToken = httpContext.Request.Headers["access_token"]; if ((!string.IsNullOrEmpty(requestAppID)) && (!string.IsNullOrEmpty(requestAccessToken))) { if (attributes != null) { //当不指定具体的客户端AppID仅运用验证属性时默认所有客户端都接受 if (attributes.ToArray().ToString()=="") { //任意一个在数据库列表中的App都可以运行,否则先判断提交的APPID与需要ID是否相符 bool mat = false; foreach (var terminalAppAttribute in attributes) { if (terminalAppAttribute.AppID == requestAppID) { mat = true; break; } } if (!mat) { errorMsg = ReturnStd.NotAuthorize("客户端应用未在服务端登记或未被授权运用当前功能."); return HandleBlockedAsync(context, requirement, errorMsg); } } } //如果未指定attributes,则表示任何一个终端服务都可以调用服务, 在验证区域验证终端提供的ID是否匹配数据库记录 string valRst = ValidateToken(requestAppID, requestAccessToken); if (string.IsNullOrEmpty(valRst)) { context.Succeed(requirement); return Task.CompletedTask; } else { errorMsg = ReturnStd.NotAuthorize("AccessToken验证失败(" + valRst + ")","91"); return HandleBlockedAsync(context, requirement, errorMsg); } } else { errorMsg = ReturnStd.NotAuthorize("未提供AppID或Token."); return HandleBlockedAsync(context, requirement, errorMsg); //return Task.CompletedTask; } } } } else { errorMsg = ReturnStd.NotAuthorize("FilterContext类型不匹配."); return HandleBlockedAsync(context, requirement, errorMsg); } errorMsg = ReturnStd.NotAuthorize("未知错误."); return HandleBlockedAsync(context,requirement, errorMsg); } //校验票据(数据库数据匹配) /// /// 验证终端服务程序提供的AccessToken是否合法 /// /// 终端APP的ID /// 终端APP利用其自身AppKEY运算出来的AccessToken,与服务器生成的进行比对 ///private string ValidateToken(string appID,string accessToken) { try { DBContextMain dBContext = new DBContextMain(); string appKeyOnServer = string.Empty; //从数据库读取AppID对应的KEY(此KEY为加解密算法的AES_KEY AuthApp authApp = dBContext.AuthApps.FirstOrDefault(a => a.AppID == appID); if (authApp == null) { return "客户端应用没有在云端登记!"; } else { appKeyOnServer = authApp.APPKey; } if (string.IsNullOrEmpty(appKeyOnServer)) { return "客户端应用基础信息有误!"; } string tmpToken = string.Empty; tmpToken = System.Net.WebUtility.UrlDecode(accessToken);//解码相应的Token到原始字符(因其中可能会有+=等特殊字符,必须编码后传递) tmpToken = OCrypto.AES16Decrypt(tmpToken, appKeyOnServer); //使用APPKEY解密并分析 if (string.IsNullOrEmpty(tmpToken)) { return "客户端提交的身份令牌运算为空!"; } else { try { //原始验证码为im_cloud_sv001-appid-ticks格式 //取出时间,与服务器时间对比,超过10秒即拒绝服务 long tmpTime =Convert.ToInt64(tmpToken.Substring(tmpToken.LastIndexOf("-")+1)); //DateTime dt = DateTime.ParseExact(tmpTime, "yyyyMMddHHmmss", CultureInfo.CurrentCulture); DateTime dt= new DateTime(tmpTime); bool IsInTimeSpan = (Convert.ToDouble(ODateTime.DateDiffSeconds(dt, DateTime.Now)) <= 7200); bool IsInternalApp = (tmpToken.IndexOf("im_cloud_sv001-") >= 0); if (!IsInternalApp || !IsInTimeSpan) { return "令牌未被许可或已经失效!"; } else { return string.Empty; //成功验证 } } catch (Exception ex) { return "令牌解析出错(" + ex.Message + ")"; } } } catch (Exception ex) { return "令牌解析出错(" + ex.Message + ")"; } } private Task HandleBlockedAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, object errorMsg) { var authorizationFilterContext = context.Resource as AuthorizationFilterContext; authorizationFilterContext.Result = new JsonResult(errorMsg) { StatusCode = 202 }; //设置为403会显示不了自定义信息,改为Accepted202,由客户端处理 context.Succeed(requirement); return Task.CompletedTask; } }
internal class TerminalAppAuthorizationRequirement : IAuthorizationRequirement { public TerminalAppAuthorizationRequirement() { } }
五、相应的Token验证代码:
[AutoValidateAntiforgeryToken] //在本控制器内自动启用跨站攻击防护 [Route("api/get_accesstoken")] public class GetAccessTokenController : Controller { //尚未限制访问频率 //返回{"access_token":"ACCESS_TOKEN","expires_in":7200} 有效期2个小时 //错误时返回{"errcode":40013,"errmsg":"invalid appid"} [AllowAnonymous] public ActionResultGet() { try { string tmpToken = string.Empty; string appID = HttpContext.Request.Headers["appid"]; string appKey = HttpContext.Request.Headers["appkey"]; if ((appID.Length < 5) || appKey.Length != 32) { return "{'errcode':10000,'errmsg':'appid或appkey未提供'}"; } //token采用im_cloud_sv001-appid-ticks数字 long timeTk = DateTime.Now.Ticks; //输出毫微秒:633603924670937500 //DateTime dt = new DateTime(timeTk);//可以还原时间 string plToken = "im_cloud1-" + appID + "-" + timeTk; tmpToken = OCrypto.AES16Encrypt(plToken, appKey); //使用APPKEY加密 tmpToken = System.Net.WebUtility.UrlEncode(tmpToken); //编码相应的Token(因其中可能会有+=等特殊字符,必须编码后传递) tmpToken = "{'access_token':'" + tmpToken + "','expires_in':7200}"; return tmpToken; } catch (Exception ex) { return "{'errcode':10001,'errmsg':'" + ex.Message +"'}"; } } } GetAccessTokenController.cs
六、这样,在我们需要控制的地方加上[TerminalApp()] 即可,这样所有授权的App都能访问,当然,也可以使用[TerminalApp(“app01”)]限定某一个ID为app01的应用访问。
[Area("SYS")] // 路由: api/sys/user [Produces("application/json")] [TerminalApp()] public class UserController : Controller { // }
七、一个CS客户端通过Web API上传数据调用示例:
string postURL = "http://sv12.ato.com/api/sys/user/postnew"; DictionaryheaderDic2 = new Dictionary { { "appid", MainFramework.CloudAppID }, { "access_token", accessToken } }; string pushRst = OPWeb.Post(postURL, headerDic2, "POST", sYS_Users); if (string.IsNullOrEmpty(pushRst)) { MyMsg.Information("推送成功!"); } else { MyMsg.Information("推送失败!", pushRst); }
string accessToken = MainFramework.CloudAccessToken; if (accessToken.IndexOf("ERROR:") >= 0) { MyMsg.Information("获取Token出错:" + accessToken); return; }
关于ASP.NET Core中怎么使用自定义验证属性控制访问权限问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注创新互联行业资讯频道了解更多相关知识。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流