asp.net mvc webapi 实用的接口加密方法示例
在很多项目中,因为webapi是对外开放的,这个时候,我们就要得考虑接口交换数据的安全性。
安全机制也比较多,如andriod与webapi 交换数据的时候,可以走双向证书方法,但是开发成本比较大,
今天我们不打算介绍这方面的知识,我们说说一个较简单也较常见的安全交换机制
在这里要提醒读者,目前所有的加密机制都不是绝对的安全!
我们的目标是,任何用户或者软件获取到我们的webapi接口url后用来再次访问该地址都是无效的!
达到这种目标的话,我们必须要在url中增加一个时间戳,但是仅仅如此还是不够,用户可以修改我们的时间戳!
因此我们可以对时间戳 进行md5加密,但是这样依然不够,用户可以直接对我们的时间戳md5的哦,因些需要引入一个绝对安全
的双方约定的key,并同时加入其它参数进行混淆!
注意:这个key要在app里和我们的webapi里各保存相同的一份!
于是我们约定公式: 加密结果=md5(时间戳+随机数+key+post或者get的参数)
下面我们开始通过上述公式写代码:
于由我的环境是asp.net mvc 的,所以重写一个加密类apisecurityfilter
1、获取参数
if (request.headers.contains("timestamp")) timestamp = httputility.urldecode(request.headers.getvalues("timestamp").firstordefault()); if (request.headers.contains("nonce")) nonce = httputility.urldecode(request.headers.getvalues("nonce").firstordefault()); if (request.headers.contains("signature")) signature = httputility.urldecode(request.headers.getvalues("signature").firstordefault()); if (string.isnullorempty(timestamp) || string.isnullorempty(nonce) || string.isnullorempty(signature)) throw new securityexception();
2、判断时间戳是否超过指定时间
double ts = 0; bool timespanvalidate = double.tryparse(timestamp, out ts); bool falg = (datetime.utcnow - new datetime(1970, 1, 1, 0, 0, 0, 0)).totalmilliseconds - ts > 60 * 1000; if (falg || (!timespanvalidate)) throw new securityexception();
3、post/delete/update 三种方式提取参数
case "post": case "put": case "delete": stream stream = httpcontext.current.request.inputstream; streamreader streamreader = new streamreader(stream); sortedparams = new sorteddictionary<string, string>(new jsonserializer().deserialize<dictionary<string, string>>(new jsontextreader(streamreader))); break;
4、get 方式提取参数
case "get": idictionary<string, string> parameters = new dictionary<string, string>(); foreach (string key in httpcontext.current.request.querystring) { if (!string.isnullorempty(key)) { parameters.add(key, httpcontext.current.request.querystring[key]); } } sortedparams = new sorteddictionary<string, string>(parameters); break;
5、排序上述参数并拼接,形成我们要参与md5的约定公式中的第四个参数
stringbuilder query = new stringbuilder(); if (sortedparams != null) { foreach (var sort in sortedparams.orderby(k => k.key)) { if (!string.isnullorempty(sort.key)) { query.append(sort.key).append(sort.value); } } data = query.tostring().replace(" ", ""); }
6、开始约定公式计算结果并对比传过的结果是否一致
var md5staff = seedwork.utils.charhelper.md5(string.concat(timestamp + nonce + staffid + data), 32); if (!md5staff.equals(signature)) throw new securityexception();
完整的代码如下:
public class apisecurityfilter : actionfilterattribute { public override void onactionexecuting(httpactioncontext actioncontext) { var request = actioncontext.request; var method = request.method.method; var staffid = "^***********************************$"; string timestamp = string.empty, nonce = string.empty, signature = string.empty; if (request.headers.contains("timestamp")) timestamp = request.headers.getvalues("timestamp").firstordefault(); if (request.headers.contains("nonce")) nonce = request.headers.getvalues("nonce").firstordefault(); if (request.headers.contains("signature")) signature = request.headers.getvalues("signature").firstordefault(); if (string.isnullorempty(timestamp) || string.isnullorempty(nonce) || string.isnullorempty(signature)) throw new securityexception(); double ts = 0; bool timespanvalidate = double.tryparse(timestamp, out ts); bool falg = (datetime.utcnow - new datetime(1970, 1, 1, 0, 0, 0, 0)).totalmilliseconds - ts > 60 * 1000; if (falg || (!timespanvalidate)) throw new securityexception("timespanvalidate"); var data = string.empty; idictionary<string, string> sortedparams = null; switch (method.toupper()) { case "post": case "put": case "delete": stream stream = httpcontext.current.request.inputstream; streamreader streamreader = new streamreader(stream); sortedparams = new sorteddictionary<string, string>(new jsonserializer().deserialize<dictionary<string, string>>(new jsontextreader(streamreader))); break; case "get": idictionary<string, string> parameters = new dictionary<string, string>(); foreach (string key in httpcontext.current.request.querystring) { if (!string.isnullorempty(key)) { parameters.add(key, httpcontext.current.request.querystring[key]); } } sortedparams = new sorteddictionary<string, string>(parameters); break; default: throw new securityexception("defaultoptions"); } stringbuilder query = new stringbuilder(); if (sortedparams != null) { foreach (var sort in sortedparams.orderby(k => k.key)) { if (!string.isnullorempty(sort.key)) { query.append(sort.key).append(sort.value); } } data = query.tostring().replace(" ", ""); } var md5staff = seedwork.utils.charhelper.md5(string.concat(timestamp + nonce + staffid + data), 32); if (!md5staff.equals(signature)) throw new securityexception("md5staff"); base.onactionexecuting(actioncontext); } public override void onactionexecuted(httpactionexecutedcontext actionexecutedcontext) { base.onactionexecuted(actionexecutedcontext); } }
7、最后在asp.net mvc 里加入配置上述类
public static class webapiconfig { public static void register(httpconfiguration config) { // web api configuration and services config.filters.add(new apisecurityfilter()); config.filters.add(new apihandleerrorattribute()); // web api routes config.maphttpattributeroutes(); config.routes.maphttproute( name: "defaultapi", routetemplate: "api/{controller}/{id}", defaults: new { id = routeparameter.optional } ); } }
8、添加写入日志类
public class apihandleerrorattribute: exceptionfilterattribute { /// <summary> /// add by laiyunba /// </summary> /// <param name="filtercontext">context oop</param> public override void onexception(httpactionexecutedcontext filtercontext) { loggerfactory.createlog().logerror(messages.error_unmanagederror, filtercontext.exception); } }
9、利用微信小程序测试接口
var data = { username: username, password: password, action: 'mobile', sms: '' }; var timestamp = util.gettimestamp(); var nonce = util.getnonce(); if (username && password) { wx.request({ url: rooturl + '/api/login', method: "post", data: data, header: { 'content-type': 'application/json', 'timestamp': timestamp, 'nonce': nonce, 'signature': util.getmd5staff(data, timestamp, nonce) }, success: function (res) { if (res.data) {
1)其中getmd5staff函数:
function getmd5staff(querydata, timestamp, nonce) { var staffid = getstaffid();//保存的key与webapi同步 var data = dictionaryorderwithdata(querydata); return md5.md5(timestamp + nonce + staffid + data); }
2)dictionaryorderwithdata函数:
function dictionaryorderwithdata(dic) { //eg {x:2,y:3,z:1} var result = ""; var sdic = object.keys(dic).sort(function (a, b) { return a.localecompare(b) }); var value = ""; for (var ki in sdic) { if (dic[sdic[ki]] == null) { value = "" } else { value = dic[sdic[ki]]; } result += sdic[ki] + value; } return result.replace(/\s/g, ""); }
10、测试日志
laiyunbaapp error: 2 : 2017-10-18 09:15:25 unmanaged error in aplication, the exception information is exception:system.security.securityexception: 安全性错误。 在 distributedservices.mainboundedcontext.filterattribute.apisecurityfilter.onactionexecuting(httpactioncontext actioncontext) 在 system.web.http.filters.actionfilterattribute.onactionexecutingasync(httpactioncontext actioncontext, cancellationtoken cancellationtoken) --- 引发异常的上一位置中堆栈跟踪的末尾 --- 在 system.runtime.compilerservices.taskawaiter.throwfornonsuccess(task task) 在 system.runtime.compilerservices.taskawaiter.handlenonsuccessanddebuggernotification(task task) 在 system.web.http.filters.actionfilterattribute.<executeactionfilterasynccore>d__0.movenext() --- 引发异常的上一位置中堆栈跟踪的末尾 --- 在 system.runtime.compilerservices.taskawaiter.throwfornonsuccess(task task) 在 system.runtime.compilerservices.taskawaiter.handlenonsuccessanddebuggernotification(task task) 在 system.web.http.controllers.actionfilterresult.<executeasync>d__2.movenext() --- 引发异常的上一位置中堆栈跟踪的末尾 --- 在 system.runtime.compilerservices.taskawaiter.throwfornonsuccess(task task) 在 system.runtime.compilerservices.taskawaiter.handlenonsuccessanddebuggernotification(task task) 在 system.web.http.controllers.exceptionfilterresult.<executeasync>d__0.movenext() 失败的程序集的区域是: mycomputer logicaloperationstack=2017-10-18 09:15:25 2017-10-18 09:15:25 datetime=2017-10-18t01:15:25.1000017z 2017-10-18 09:15:25 callstack= 在 system.environment.getstacktrace(exception e, boolean needfileinfo) 在 system.environment.get_stacktrace() 在 system.diagnostics.traceeventcache.get_callstack() 在 system.diagnostics.tracelistener.writefooter(traceeventcache eventcache) 在 system.diagnostics.tracesource.traceevent(traceeventtype eventtype, int32 id, string message) 在 infrastructure.crosscutting.netframework.logging.tracesourcelog.traceinternal(traceeventtype eventtype, string message) 在 infrastructure.crosscutting.netframework.logging.tracesourcelog.logerror(string message, exception exception, object[] args) 在 system.web.http.filters.exceptionfilterattribute.onexceptionasync(httpactionexecutedcontext actionexecutedcontext, cancellationtoken cancellationtoken) 在 system.web.http.filters.exceptionfilterattribute.<executeexceptionfilterasynccore>d__0.movenext() 在 system.runtime.compilerservices.asynctaskmethodbuilder.start[tstatemachine](tstatemachine& statemachine) 在 system.web.http.filters.exceptionfilterattribute.executeexceptionfilterasynccore(httpactionexecutedcontext actionexecutedcontext, cancellationtoken cancellationtoken) 在 system.web.http.filters.exceptionfilterattribute.system.web.http.filters.iexceptionfilter.executeexceptionfilterasync(httpactionexecutedcontext actionexecutedcontext, cancellationtoken cancellationtoken) 在 system.web.http.controllers.exceptionfilterresult.<executeasync>d__0.movenext() 在 system.runtime.compilerservices.asynctaskmethodbuilder`1.start[tstatemachine](tstatemachine& statemachine) 在 system.web.http.controllers.exceptionfilterresult.executeasync(cancellationtoken cancellationtoken) 在 system.web.http.apicontroller.executeasync(httpcontrollercontext controllercontext, cancellationtoken cancellationtoken) 在 system.web.http.dispatcher.httpcontrollerdispatcher.<sendasync>d__1.movenext() 在 system.runtime.compilerservices.asynctaskmethodbuilder`1.start[tstatemachine](tstatemachine& statemachine) 在 system.web.http.dispatcher.httpcontrollerdispatcher.sendasync(httprequestmessage request, cancellationtoken cancellationtoken) 在 system.net.http.httpmessageinvoker.sendasync(httprequestmessage request, cancellationtoken cancellationtoken) 在 system.web.http.dispatcher.httproutingdispatcher.sendasync(httprequestmessage request, cancellationtoken cancellationtoken)
至此,webapi加密工作已经全部完成,上述异常是直接访问url报的错误,必须在app环境下才可以正常访问。
总结:webapi加密机密很多,像微信小程序,用户很难拿到客户端app的源码,想知道我们的key也是无从说起。当然,我们也得定期更新app版本。
像app for andriod or ios 可以使用双向证书,或者使用我们上述的方式,然后加固app,防止不怀好意的人破解得到key,当然不管如何,我们首先要走的都是https协议!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 使用重绘项美化WinForm的控件
推荐阅读
-
如何使用签名保证ASP.NET MVC OR WEBAPI的接口安全
-
asp.net core webapi项目配置全局路由的方法示例
-
asp.net mvc webapi 实用的接口加密方法示例
-
ASP.NET MVC API 接口验证的示例代码
-
asp.net mvc webapi 实用的接口加密方法示例
-
ASP.NET Core MVC获取请求的参数方法示例
-
如何使用签名保证ASP.NET MVC OR WEBAPI的接口安全
-
asp.net core webapi项目配置全局路由的方法示例
-
asp.net mvc如何动态编译生成Controller的方法示例详解
-
asp.net mvc如何动态编译生成Controller的方法示例详解