您的位置:首页 > 编程语言 > ASP

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

2017-10-19 15:15 369 查看
  在很多项目中,因为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(new JsonSerializer().Deserialize>(new JsonTextReader(streamReader)));

  break;

  4、GET 方式提取参数

  case "GET":

  IDictionary parameters = new Dictionary();

  foreach (string key in HttpContext.Current.Request.QueryString)

  {

  if (!string.IsNullOrEmpty(key))

  {

  parameters.Add(key, HttpContext.Current.Request.QueryString[key]);

  }

  }

  sortedParams = new SortedDictionary(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();

  完整的代码如下:

  1 public class ApiSecurityFilter : ActionFilterAttribute

  2 {

  3 public override void OnActionExecuting(HttpActionContext actionContext)

  4 {

  5 var request = actionContext.Request;

  6

  7 var method = request.Method.Method;

  8 var staffId = "^***********************************$";

  9

  10 string timestamp = string.Empty, nonce = string.Empty, signature = string.Empty;

  11

  12 if (request.Headers.Contains("ti
4000
mestamp"))

  13 timestamp = request.Headers.GetValues("timestamp").FirstOrDefault();

  14

  15 if (request.Headers.Contains("nonce"))

  16 nonce = request.Headers.GetValues("nonce").FirstOrDefault();

  17

  18 if (request.Headers.Contains("signature"))

  19 signature = request.Headers.GetValues("signature").FirstOrDefault();

  20

  21 if (string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(nonce) || string.IsNullOrEmpty(signature))

  22 throw new SecurityException();

  23

  24 double ts = 0;

  25 bool timespanvalidate = double.TryParse(timestamp, out ts);

  26

  27 bool falg = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds - ts > 60 * 1000;

  28

  29 if (falg || (!timespanvalidate))

  30 throw new SecurityException("timeSpanValidate");

  31

  32 var data = string.Empty;

  33 IDictionary sortedParams = null;

  34

  35 switch (method.ToUpper())

  36 {

  37 case "POST":

  38 case "PUT":

  39 case "DELETE":

  40

  41 Stream stream = HttpContext.Current.Request.InputStream;

  42 StreamReader streamReader = new StreamReader(stream);

  43 sortedParams = new SortedDictionary(new JsonSerializer().Deserialize>(new JsonTextReader(streamReader)));

  44

  45 break;

  46

  47 case "GET":

  48

  49 IDictionary parameters = new Dictionary();

  50

  51 foreach (string key in HttpContext.Current.Request.QueryString)

  52 {

  53 if (!string.IsNullOrEmpty(key))

  54 {

  55 parameters.Add(key, HttpContext.Current.Request.QueryString[key]);

  56 }

  57 }

  58

  59 sortedParams = new SortedDictionary(parameters);

  60

  61 break;

  62

  63 default:

  64 throw new SecurityException("defaultOptions");

  65 }

  66

  67 StringBuilder query = new StringBuilder();

  68

  69 if (sortedParams != null)

  70 {

  71 foreach (var sort in sortedParams.OrderBy(k => k.Key))

  72 {

  73 if (!string.IsNullOrEmpty(sort.Key))

  74 {

  75 query.Append(sort.Key).Append(sort.Value);

  76 }

  77 }

  78

  79 data = query.ToString().Replace(" ", "");

  80 }

  81

  82 var md5Staff = Seedwork.Utils.CharHelper.MD5(string.Concat(timestamp + nonce + staffId + data), 32);

  83

  84 if (!md5Staff.Equals(signature))

  85 throw new SecurityException("md5Staff");

  86

  87 base.OnActionExecuting(actionContext);

  88 }

  89

  90 public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)

  91 {

  92 base.OnActionExecuted(actionExecutedContext);

  93 }

  94 }

  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

  {

  ///

  /// add by laiyunba

  ///

  /// context oop

  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.d__0.MoveNext()

  --- 引发异常的上一位置中堆栈跟踪的末尾 ---

  在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)

  在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

  在 System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()

  --- 引发异常的上一位置中堆栈跟踪的末尾 ---

  在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)

  在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

  在 System.Web.Http.Controllers.ExceptionFilterResult.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.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.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.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协议!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: