2023年6月20日发(作者:)

e中使⽤⾃定义验证属性控制访问权限在应⽤中,有时我们需要对访问的客户端进⾏有效性验证,只有提供有效凭证(AccessToken)的终端应⽤能访问我们的受控站点(如WebAPI站点),此时我们可以通过验证属性的⽅法来解决。⼀、public class Startup的配置://启⽤跨域访问(不同端⼝也是跨域)s(options =>{icy("AllowOriginOtherBis",builder => igins("1.16.9.12:4432", ":4432", "localhost:44384", "1.16.9.12:4432",":4432").AllowAnyMethod().AllowAnyHeader());});//启⽤⾃定义属性以便对控制器或Action进⾏[TerminalApp()]定义。gleton();horization(options =>{icy("TerminalApp", policyBuilder =>{(new TerminalAppAuthorizationRequirement());});});⼆、public void Configure(IApplicationBuilder app, IHostingEnvironment env)中的配置:psRedirection();  //使⽤Https传输s("AllowOriginOtherBis"); //根据定义启⽤跨域设置三、⽰例WebApi项⽬结构:四、主要代码(我采⽤的从数据库进⾏验证): [AttributeUsage( | , 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 ((ce as AuthorizationFilterContext)?.ActionDescriptor is ControllerActionDescriptor action) { ge(GetAttributes(yingSystemType)); ge(GetAttributes(Info)); } return HandleRequirementAsync(context, requirement, attributes); } protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable attributes); private static IEnumerable GetAttributes(MemberInfo memberInfo) { return tomAttributes(typeof(TAttribute), false).Cast(); } } internal class TerminalAppAuthorizationHandler : AttributeAuthorizationHandler { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, IEnumerable attributes) { object errorMsg = ; //如果取不到⾝份验证信息,并且不允许匿名访问,则返回未验证403 if (ce is AuthorizationFilterContext filterContext &&Descriptor is ControllerActionDescriptor descriptor) { //先判断是否是匿名访问, if (descriptor != null) { var actionAttributes = tomAttributes(inherit: true); bool isAnonymous = (a => a is AllowAnonymousAttribute); //⾮匿名的⽅法,链接中添加accesstoken值 if (isAnonymous) { d(requirement); return tedTask; } else { //url获取access_token //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息 var httpContext = (ce as AuthorizationFilterContext).HttpContext; //var questUrl = r(); string requestAppID = s["appid"]; string requestAccessToken = s["access_token"]; if ((!OrEmpty(requestAppID)) && (!OrEmpty(requestAccessToken))) { if (attributes != null) { //当不指定具体的客户端AppID仅运⽤验证属性时默认所有客户端都接受 if (y().ToString()=="")

{ //任意⼀个在数据库列表中的App都可以运⾏,否则先判断提交的APPID与需要ID是否相符 bool mat = false; foreach (var terminalAppAttribute in attributes) { if ( == requestAppID) { mat = true; break; } } if (!mat) { errorMsg = horize("客户端应⽤未在服务端登记或未被授权运⽤当前功能."); return HandleBlockedAsync(context, requirement, errorMsg); } } } //如果未指定attributes,则表⽰任何⼀个终端服务都可以调⽤服务, 在验证区域验证终端提供的ID是否匹配数据库记录 string valRst = ValidateToken(requestAppID, requestAccessToken); if (OrEmpty(valRst)) { d(requirement); return tedTask; } else { errorMsg = horize("AccessToken验证失败(" + valRst + ")","91"); return HandleBlockedAsync(context, requirement, errorMsg); } } else { errorMsg = horize("未提供AppID或Token.");

return HandleBlockedAsync(context, requirement, errorMsg); //return tedTask; } } } } else { errorMsg = horize("FilterContext类型不匹配."); return HandleBlockedAsync(context, requirement, errorMsg); } errorMsg = horize("未知错误."); 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 = ; //从数据库读取AppID对应的KEY(此KEY为加解密算法的AES_KEY AuthApp authApp = rDefault(a => == appID); if (authApp == null) { return "客户端应⽤没有在云端登记!"; } else { appKeyOnServer = ; } if (OrEmpty(appKeyOnServer)) { return "客户端应⽤基础信息有误!";

} string tmpToken = ; tmpToken = ode(accessToken);//解码相应的Token到原始字符(因其中可能会有+=等特殊字符,必须编码后传递) tmpToken = 16Decrypt(tmpToken, appKeyOnServer); //使⽤APPKEY解密并分析 if (OrEmpty(tmpToken)) { return "客户端提交的⾝份令牌运算为空!"; } else { try { //原始验证码为im_cloud_sv001-appid-ticks格式 //取出时间,与服务器时间对⽐,超过10秒即拒绝服务 long tmpTime =64(ing(dexOf("-")+1)); //DateTime dt = xact(tmpTime, "yyyyMMddHHmmss", tCulture); DateTime dt= new DateTime(tmpTime); bool IsInTimeSpan = (le(ffSeconds(dt, )) <= 7200); bool IsInternalApp = (f("im_cloud_sv001-") >= 0); if (!IsInternalApp || !IsInTimeSpan) { return "令牌未被许可或已经失效!"; } else { return ; //成功验证 } } catch (Exception ex) { return "令牌解析出错(" + e + ")"; } } } catch (Exception ex) { return "令牌解析出错(" + e + ")"; } } private Task HandleBlockedAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, object errorMsg) { var authorizationFilterContext = ce as AuthorizationFilterContext; = new JsonResult(errorMsg) { StatusCode = 202 }; //设置为403会显⽰不了⾃定义信息,改为Accepted202,由客户端处理 d(requirement); return tedTask; } } 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 ActionResult Get() { try { string tmpToken = ; string appID = s["appid"]; string appKey = s["appkey"]; if (( < 5) || != 32) { return "{'errcode':10000,'errmsg':'appid或appkey未提供'}"; } //token采⽤im_cloud_sv001-appid-ticks数字 long timeTk = ; //输出毫微秒:633600 //DateTime dt = new DateTime(timeTk);//可以还原时间 string plToken = "im_cloud1-" + appID + "-" + timeTk; tmpToken = 16Encrypt(plToken, appKey); //使⽤APPKEY加密 tmpToken = ode(tmpToken); //编码相应的Token(因其中可能会有+=等特殊字符,必须编码后传递) tmpToken = "{'access_token':'" + tmpToken + "','expires_in':7200}"; return tmpToken; } catch (Exception ex) { return "{'errcode':10001,'errmsg':'" + e +"'}"; } } }六、这样,在我们需要控制的地⽅加上[TerminalApp()] 即可,这样所有授权的App都能访问,当然,也可以使⽤[TerminalApp(“app01”)]限定某⼀个ID为app01的应⽤访问。 [Area("SYS")] // 路由: api/sys/user [Produces("application/json")] [TerminalApp()]

public class UserController : Controller{//}  七、⼀个CS客户端通过Web API上传数据调⽤⽰例: string postURL = "/api/sys/user/postnew"; Dictionary headerDic2 = new Dictionary { { "appid", ppID }, { "access_token", accessToken } }; string pushRst = (postURL, headerDic2, "POST", sYS_Users); if (OrEmpty(pushRst)) { ation("推送成功!"); } else { ation("推送失败!", pushRst); }   string accessToken = ccessToken; if (f("ERROR:") >= 0) { ation("获取Token出错:" + accessToken); return; }  

2023年6月20日发(作者:)

e中使⽤⾃定义验证属性控制访问权限在应⽤中,有时我们需要对访问的客户端进⾏有效性验证,只有提供有效凭证(AccessToken)的终端应⽤能访问我们的受控站点(如WebAPI站点),此时我们可以通过验证属性的⽅法来解决。⼀、public class Startup的配置://启⽤跨域访问(不同端⼝也是跨域)s(options =>{icy("AllowOriginOtherBis",builder => igins("1.16.9.12:4432", ":4432", "localhost:44384", "1.16.9.12:4432",":4432").AllowAnyMethod().AllowAnyHeader());});//启⽤⾃定义属性以便对控制器或Action进⾏[TerminalApp()]定义。gleton();horization(options =>{icy("TerminalApp", policyBuilder =>{(new TerminalAppAuthorizationRequirement());});});⼆、public void Configure(IApplicationBuilder app, IHostingEnvironment env)中的配置:psRedirection();  //使⽤Https传输s("AllowOriginOtherBis"); //根据定义启⽤跨域设置三、⽰例WebApi项⽬结构:四、主要代码(我采⽤的从数据库进⾏验证): [AttributeUsage( | , 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 ((ce as AuthorizationFilterContext)?.ActionDescriptor is ControllerActionDescriptor action) { ge(GetAttributes(yingSystemType)); ge(GetAttributes(Info)); } return HandleRequirementAsync(context, requirement, attributes); } protected abstract Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement, IEnumerable attributes); private static IEnumerable GetAttributes(MemberInfo memberInfo) { return tomAttributes(typeof(TAttribute), false).Cast(); } } internal class TerminalAppAuthorizationHandler : AttributeAuthorizationHandler { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, IEnumerable attributes) { object errorMsg = ; //如果取不到⾝份验证信息,并且不允许匿名访问,则返回未验证403 if (ce is AuthorizationFilterContext filterContext &&Descriptor is ControllerActionDescriptor descriptor) { //先判断是否是匿名访问, if (descriptor != null) { var actionAttributes = tomAttributes(inherit: true); bool isAnonymous = (a => a is AllowAnonymousAttribute); //⾮匿名的⽅法,链接中添加accesstoken值 if (isAnonymous) { d(requirement); return tedTask; } else { //url获取access_token //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息 var httpContext = (ce as AuthorizationFilterContext).HttpContext; //var questUrl = r(); string requestAppID = s["appid"]; string requestAccessToken = s["access_token"]; if ((!OrEmpty(requestAppID)) && (!OrEmpty(requestAccessToken))) { if (attributes != null) { //当不指定具体的客户端AppID仅运⽤验证属性时默认所有客户端都接受 if (y().ToString()=="")

{ //任意⼀个在数据库列表中的App都可以运⾏,否则先判断提交的APPID与需要ID是否相符 bool mat = false; foreach (var terminalAppAttribute in attributes) { if ( == requestAppID) { mat = true; break; } } if (!mat) { errorMsg = horize("客户端应⽤未在服务端登记或未被授权运⽤当前功能."); return HandleBlockedAsync(context, requirement, errorMsg); } } } //如果未指定attributes,则表⽰任何⼀个终端服务都可以调⽤服务, 在验证区域验证终端提供的ID是否匹配数据库记录 string valRst = ValidateToken(requestAppID, requestAccessToken); if (OrEmpty(valRst)) { d(requirement); return tedTask; } else { errorMsg = horize("AccessToken验证失败(" + valRst + ")","91"); return HandleBlockedAsync(context, requirement, errorMsg); } } else { errorMsg = horize("未提供AppID或Token.");

return HandleBlockedAsync(context, requirement, errorMsg); //return tedTask; } } } } else { errorMsg = horize("FilterContext类型不匹配."); return HandleBlockedAsync(context, requirement, errorMsg); } errorMsg = horize("未知错误."); 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 = ; //从数据库读取AppID对应的KEY(此KEY为加解密算法的AES_KEY AuthApp authApp = rDefault(a => == appID); if (authApp == null) { return "客户端应⽤没有在云端登记!"; } else { appKeyOnServer = ; } if (OrEmpty(appKeyOnServer)) { return "客户端应⽤基础信息有误!";

} string tmpToken = ; tmpToken = ode(accessToken);//解码相应的Token到原始字符(因其中可能会有+=等特殊字符,必须编码后传递) tmpToken = 16Decrypt(tmpToken, appKeyOnServer); //使⽤APPKEY解密并分析 if (OrEmpty(tmpToken)) { return "客户端提交的⾝份令牌运算为空!"; } else { try { //原始验证码为im_cloud_sv001-appid-ticks格式 //取出时间,与服务器时间对⽐,超过10秒即拒绝服务 long tmpTime =64(ing(dexOf("-")+1)); //DateTime dt = xact(tmpTime, "yyyyMMddHHmmss", tCulture); DateTime dt= new DateTime(tmpTime); bool IsInTimeSpan = (le(ffSeconds(dt, )) <= 7200); bool IsInternalApp = (f("im_cloud_sv001-") >= 0); if (!IsInternalApp || !IsInTimeSpan) { return "令牌未被许可或已经失效!"; } else { return ; //成功验证 } } catch (Exception ex) { return "令牌解析出错(" + e + ")"; } } } catch (Exception ex) { return "令牌解析出错(" + e + ")"; } } private Task HandleBlockedAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, object errorMsg) { var authorizationFilterContext = ce as AuthorizationFilterContext; = new JsonResult(errorMsg) { StatusCode = 202 }; //设置为403会显⽰不了⾃定义信息,改为Accepted202,由客户端处理 d(requirement); return tedTask; } } 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 ActionResult Get() { try { string tmpToken = ; string appID = s["appid"]; string appKey = s["appkey"]; if (( < 5) || != 32) { return "{'errcode':10000,'errmsg':'appid或appkey未提供'}"; } //token采⽤im_cloud_sv001-appid-ticks数字 long timeTk = ; //输出毫微秒:633600 //DateTime dt = new DateTime(timeTk);//可以还原时间 string plToken = "im_cloud1-" + appID + "-" + timeTk; tmpToken = 16Encrypt(plToken, appKey); //使⽤APPKEY加密 tmpToken = ode(tmpToken); //编码相应的Token(因其中可能会有+=等特殊字符,必须编码后传递) tmpToken = "{'access_token':'" + tmpToken + "','expires_in':7200}"; return tmpToken; } catch (Exception ex) { return "{'errcode':10001,'errmsg':'" + e +"'}"; } } }六、这样,在我们需要控制的地⽅加上[TerminalApp()] 即可,这样所有授权的App都能访问,当然,也可以使⽤[TerminalApp(“app01”)]限定某⼀个ID为app01的应⽤访问。 [Area("SYS")] // 路由: api/sys/user [Produces("application/json")] [TerminalApp()]

public class UserController : Controller{//}  七、⼀个CS客户端通过Web API上传数据调⽤⽰例: string postURL = "/api/sys/user/postnew"; Dictionary headerDic2 = new Dictionary { { "appid", ppID }, { "access_token", accessToken } }; string pushRst = (postURL, headerDic2, "POST", sYS_Users); if (OrEmpty(pushRst)) { ation("推送成功!"); } else { ation("推送失败!", pushRst); }   string accessToken = ccessToken; if (f("ERROR:") >= 0) { ation("获取Token出错:" + accessToken); return; }