最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 科技 - 知识百科 - 正文

ASP.NET Core使用自定义验证属性控制访问权限详解

来源:动视网 责编:小采 时间:2020-11-27 22:34:46
文档

ASP.NET Core使用自定义验证属性控制访问权限详解

ASP.NET Core使用自定义验证属性控制访问权限详解:前言 大家都知道在应用中,有时我们需要对访问的客户端进行有效性验证,只有提供有效凭证(AccessToken)的终端应用能访问我们的受控站点(如WebAPI站点),此时我们可以通过验证属性的方法来解决。 本文将详细介绍ASP.NET Core使用自定义验证属性控制访问权
推荐度:
导读ASP.NET Core使用自定义验证属性控制访问权限详解:前言 大家都知道在应用中,有时我们需要对访问的客户端进行有效性验证,只有提供有效凭证(AccessToken)的终端应用能访问我们的受控站点(如WebAPI站点),此时我们可以通过验证属性的方法来解决。 本文将详细介绍ASP.NET Core使用自定义验证属性控制访问权


前言

大家都知道在应用中,有时我们需要对访问的客户端进行有效性验证,只有提供有效凭证(AccessToken)的终端应用能访问我们的受控站点(如WebAPI站点),此时我们可以通过验证属性的方法来解决。

本文将详细介绍ASP.NET Core使用自定义验证属性控制访问权限的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧

方法如下

一、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<IAuthorizationHandler, TerminalAppAuthorizationHandler>();
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; }

 /// <summary>
 /// 指定客户端访问API
 /// </summary>
 /// <param name="appID"></param>
 public TerminalAppAttribute(string appID="") : base("TerminalApp")
 {
 AppID = appID;
 }
 }
public abstract class AttributeAuthorizationHandler<TRequirement, TAttribute> : AuthorizationHandler<TRequirement> where TRequirement : IAuthorizationRequirement where TAttribute : Attribute
 {
 protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TRequirement requirement)
 {
 var attributes = new List<TAttribute>();

 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<TAttribute> attributes);

 private static IEnumerable<TAttribute> GetAttributes(MemberInfo memberInfo)
 {
 return memberInfo.GetCustomAttributes(typeof(TAttribute), false).Cast<TAttribute>();
 }
 }

 internal class TerminalAppAuthorizationHandler : AttributeAuthorizationHandler<TerminalAppAuthorizationRequirement,TerminalAppAttribute>
 {
 protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TerminalAppAuthorizationRequirement requirement, IEnumerable<TerminalAppAttribute> 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);
 }


 //校验票据(数据库数据匹配)
 /// <summary>
 /// 验证终端服务程序提供的AccessToken是否合法
 /// </summary>
 /// <param name="appID">终端APP的ID</param>
 /// <param name="accessToken">终端APP利用其自身AppKEY运算出来的AccessToken,与服务器生成的进行比对</param>
 /// <returns></returns>
 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 ActionResult<string> Get()
 {
 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";
 
Dictionary<string, string> headerDic2 = new Dictionary<string, string>
{
 { "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使用自定义验证属性控制访问权限详解

ASP.NET Core使用自定义验证属性控制访问权限详解:前言 大家都知道在应用中,有时我们需要对访问的客户端进行有效性验证,只有提供有效凭证(AccessToken)的终端应用能访问我们的受控站点(如WebAPI站点),此时我们可以通过验证属性的方法来解决。 本文将详细介绍ASP.NET Core使用自定义验证属性控制访问权
推荐度:
标签: 验证 自定义 详解
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top