当前位置: 移动技术网 > IT编程>开发语言>.net > ASP.NET Core使用自定义验证属性控制访问权限详解

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

2018年11月05日  | 移动技术网IT编程  | 我要评论

智者无敌 下载,dailifuwuqi,arp断网攻击

前言

大家都知道在应用中,有时我们需要对访问的客户端进行有效性验证,只有提供有效凭证(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;
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网