在很多项目中,因为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 = ;
bool timespanvalidate = double.TryParse(timestamp, out ts); bool falg = (DateTime.UtcNow - new DateTime(, , , , , , )).TotalMilliseconds - ts > * ; 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), );

            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 = ;
bool timespanvalidate = double.TryParse(timestamp, out ts); bool falg = (DateTime.UtcNow - new DateTime(, , , , , , )).TotalMilliseconds - ts > * ; 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), ); 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、添加写入日志类(此类不是本文重点,详细参考 http://www.cnblogs.com/laogu2/p/5912153.html

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:  : -- ::    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=-- ::
-- :: DateTime=--18T01::.1000017Z
-- :: 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`.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`.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协议!

作者:谷歌's谷歌's博客园
出处:http://www.cnblogs.com/laogu2/ 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请给我留言

asp.net mvc webapi 实用的接口加密方法(转载)的更多相关文章

  1. asp.net mvc webapi 实用的接口加密方法

    在很多项目中,因为webapi是对外开放的,这个时候,我们就要得考虑接口交换数据的安全性. 安全机制也比较多,如andriod与webapi 交换数据的时候,可以走双向证书方法,但是开发成本比较大, ...

  2. net mvc webapi 实用

    asp.net mvc webapi 实用的接口加密方法   在很多项目中,因为webapi是对外开放的,这个时候,我们就要得考虑接口交换数据的安全性. 安全机制也比较多,如andriod与webap ...

  3. .net异步性能测试(包括ASP.NET MVC WebAPI异步方法)

    很久没有写博客了,今年做的产品公司这两天刚刚开了发布会,稍微清闲下来,想想我们做的产品还有没有性能优化空间,于是想到了.Net的异步可以优化性能,但到底能够提升多大的比例呢?恰好有一个朋友正在做各种语 ...

  4. 案例:1 Ionic Framework+AngularJS+ASP.NET MVC WebApi Jsonp 移动开发

    落叶的庭院扫的一干二净之后,还要轻轻把树摇一下,抖落几片叶子,这才是Wabi Sabi的境界. 介绍:Ionic是移动框架,angularjs这就不用说了,ASP.Net MVC WebApi提供数据 ...

  5. ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml) 用javascript在客户端删除某一个cookie键值对 input点击链接另一个页面,各种操作。 C# 往线程里传参数的方法总结 TCP/IP 协议 用C#+Selenium+ChromeDriver 生成我的咕咚跑步路线地图 (转)值得学习百度开源70+项目

    ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml)   我们都知道在使用WebApi的时候Controller会自动将Action的返回值自动进行各种序列化处理(序列化为 ...

  6. C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  7. 让Asp.net mvc WebAPI 支持OData协议进行分页查询操作

    这是我在用Asp.net mvc WebAPI 支持 OData协议 做分页查询服务时的 个人拙笔. 代码已经开发到oschina上.有兴趣的朋友可以看看,欢迎大家指出不足之处. 看过了园子里的几篇关 ...

  8. C#/ASP.NET MVC微信公众号接口开发之从零开发(三)回复消息 (附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  9. ASP.NET Core WebApi构建API接口服务实战演练

    一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...

随机推荐

  1. hpu_newoj_1028-exgcd

    The Elevator   描述 全是电梯. Philo正处于高度为0的一个平台上,在他面前的一个平面,全是上上下下的电梯. Philo想要离开这里,请你帮帮他. 电梯世界规则:这里的电梯所能到达的 ...

  2. visual studio利用 indent guides 格式化代码 添加竖线

    点击 Visual Studio 2013 工具—扩展和更新—联机 然后输入indent guides 自动搜索出来这个插件(如图).注:Visual Studio 2010需要自己在网上下载安装. ...

  3. 【转】JavaScript => TypeScript 入门

    几个月前把 ES6 的特性都过了一遍,收获颇丰.现在继续来看看 TypesScript(下文简称为 “TS”).限于经验,本文一些总结如有不当,欢迎指正. 官网有这样一段描述: TypeScript ...

  4. 通过BeanPostProcessor理解Spring中Bean的生命周期

    通过BeanPostProcessor理解Spring中Bean的生命周期及AOP原理 Spring源码解析(十一)Spring扩展接口InstantiationAwareBeanPostProces ...

  5. python 自然语言处理(四)____词典资源

    词典或者词典资源是一个词和/或短语及其相关信息的集合,例如:词性和词意定义等相关信息.词典资源附属于文本,而且通常在文本的基础上创建和丰富.下面列举几种nltk中的词典资源. 1. 词汇列表语料库 n ...

  6. C++解析五-this 指针,指向类的指针

    C++this指针 在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址.this 指针是所有成员函数的隐含参数.因此,在成员函数内部,它可以用来指向调用对象.友元函数没有 this 指 ...

  7. learning scala write to file

    scala 写文件功能: scala> import java.io.PrintWriterimport java.io.PrintWriter scala> val outputFile ...

  8. while循环以及格式化输出总结

    while循环: while 无限循环 count = 1 sum = 0 while True: sum = sum + count count = count + 1 if count == 10 ...

  9. PC/FORTH 循环

    body, table{font-family: 微软雅黑} table{border-collapse: collapse; border: solid gray; border-width: 2p ...

  10. csv,json格式数据的读写

    #!python3 # -*- coding:utf-8 -*- #CSV stands for "comma-separated values",and CSV files ar ...