博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
安全的API接口解决方案
阅读量:5846 次
发布时间:2019-06-18

本文共 5934 字,大约阅读时间需要 19 分钟。

在各种手机APP泛滥的现在,背后都有同样泛滥的API接口在支撑,其中鱼龙混杂,直接裸奔的WEB API大量存在,安全性令人堪优

在以前WEB API概念没有很普及的时候,都采用自已定义的接口和结构,对于公开访问的接口,专业点的都会做下安全验证,数据签名之类

反而现在,谁都可以用WEB API估接口,安全性早忘一边了,特别是外包小公司的APP项目,80%都有安全漏洞(面试了大半年APP开发得出的结论)

特在过年之前,整理了下在用的解决方案,本方案解决了

  • 数据安全问题
  • 标准消息结构
  • 接口测试程序
  • 接口文档体现

正文

数据结构

对于一个接口,返回的内容除了要返回业务数据外,还得返回处理状态,并且这个状态是在每个接口都得有

所以数据格式都会定义为:

数据头(描述数据信息)

-----------------------------------

数据体(具体数据)

本文定义结构为

///     /// 处理结果    ///  public class DealResult { ///  /// 处理结果 ///  public bool Result { get; set; } ///  /// 消息 ///  public string Message { get; set; } ///  /// 关联数据 ///  public object Data { get; set; } }
View Code

所有接口都返回此对象,会描述本次请求的状态,和对应的数据,服务端则根据实际情况,返回处理结果和对应的数据

 

数据安全

开方式接口安全性就不用多说了,解决方法为加密,或数据签名验证,本文方案为进行数据签名

同返回的数据一样,提交到服务器的数据格式也统一约定,定义一个数据头基类

///     /// 参数基类    ///  [Serializable] public class ParameBase { string time = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"); ///  /// 时间 格式 yyyy-MM-dd hh:mm:ss ///  public string Time { get { return time; } set { time = value; } } ///  /// 来源网站 = 1, IOS = 2,Android = 3, 微信 = 4 ///  public int SourceFrom { get; set; } ///  /// 签名 ///  public string Token { get; set; } }
View Code

 

一个登录对象表示为

///     /// 登录    ///  public class Login : ParameBase { ///  /// 用户名 ///  public string Name { get; set; } ///  /// 密码 ///  public string Password { get; set; } }
View Code

数据签名表示为(KEY稍后讲到)

Token=MD5(属性值1+值2....+KEY)

按此对象表示为 MD5(Name+PassWord+Source+Time+KEY)

如果是GET参数怎么办,一样,按参数名计算,同时传递的参数要附带上Source,Time,Token 

 

密钥机制

有的喜欢把密钥放在客户端,或固定密钥,显然都有安全问题,解决方法是动态获取

这就意味着在设计接口时,有一个接口是首先要调用的,让服务器返回密钥,于是就有了登录的概念

过程表示为

登录>返回用户信息和密钥=>存储用户信息和密钥=>使用密钥调用其它接口

这样只有登录者和服务器才知道自已的密钥了

综上所述,数据结构表示为

客户端提交结构为 ParameBase(附带签名信息)

服务端返回结构为 DealResult

 

登录机制

同网页请求一样,怎么知道多次调用是同一个人呢,这里采用了COOKIE的形式,登录后服务端返回一个COOKIE,客户端再请求时带上这个COOKIE

服务端需要存储这个COOKIE标识,所有的验证处理都会基于此标识来判断用户

 

有了上面基础,进入项目阶段

WEB API项目

其实用什么项目类型都行,只是WEB API方便了对象结构序列化和传参

默认WEB API路由RESUFUL形式,没有控制器方法,只能按METHOD来定义,很不方便,改成控制器的形式,这样就能用方法名来访问了

更改路由配置为

1
2
3
4
5
config.Routes.MapHttpRoute(
                
name:
"DefaultApi"
,
                
routeTemplate:
"api/{controller}/{action}/{id}"
,
//加上路由ACTION参数
                
defaults:
new
{ id = RouteParameter.Optional }
            
);

在此文,数据分为请求和返回,以登录返回用户信息为例,登录为请求,用户信息为返回,示例对象结构为

用户对象

///     /// 登录返回用户    ///  public class User { ///  /// 用户编号 ///  public int Id { get; set; } ///  /// 名称 ///  public string Name { get; set; } ///  /// 本次登录的KEY ///  public string Key { get; set; } ///  /// 本资登录的凭证 ///  public string Voucher { get; set; } }
View Code

 

请求方式

这里只采用了GET,POST两种方式,根据实际情况定义,控制器方法一定需要都标明,不然会出现路由BUG

定义登录方法

///         /// 登录        ///  ///  /// 
User
[HttpPost] [AnonymousSign] public DealResult Login([FromBody] Login parame) { if (parame.Password != "123") { return DealResult(false, "密码不正确"); } string key2 = System.Guid.NewGuid().ToString(); string voucher = System.Guid.NewGuid().ToString(); var user = new User() { Name = parame.Name, Id = 1, Key = key2, Voucher = voucher }; var timeDiff = (DateTime.Now - Convert.ToDateTime(parame.Time)).TotalSeconds;//保存客户端和服务端时间差 LoginStatusContext.SetLoginStatus(voucher, user.Id, key2, timeDiff); CoreHelper.CookieHelper.AddCookies("user", voucher);//存入COOKIE return DealResult(true, "", user); }
View Code

这里可以看到,创建了两个GUID,一个为用户凭证,一个为用户密钥,放入用户信息返回,同时调用LoginStatusContext.SetLoginStatus保存登录信息

同时使用了AnonymousSign标注,此方法使用默认签名Setting.DefaultKey

定义获取用信息方法

///         /// 基本信息        ///  /// 参数name /// 
User
[HttpGet] public DealResult GetBasicInfo(string name) { var user = new User() { Name = name, Id = CurrentUserId }; return DealResult(true, string.Empty, user); }
View Code

 

示例控制器完整定义

///     /// 帐号操作    ///  [SignCheckAttribute] public class AccountController : BaseController { ///  /// 登录 ///  ///  /// 
User
[HttpPost] [AnonymousSign] public DealResult Login([FromBody] Login parame) { if (parame.Password != "123") { return DealResult(false, "密码不正确"); } string key2 = System.Guid.NewGuid().ToString(); string voucher = System.Guid.NewGuid().ToString(); var user = new User() { Name = parame.Name, Id = 1, Key = key2, Voucher = voucher }; var timeDiff = (DateTime.Now - Convert.ToDateTime(parame.Time)).TotalSeconds;//保存客户端和服务端时间差 LoginStatusContext.SetLoginStatus(voucher, user.Id, key2, timeDiff); CoreHelper.CookieHelper.AddCookies("user", voucher);//存入COOKIE return DealResult(true, "", user); } /// /// 基本信息 /// /// 参数name ///
User
[HttpGet] public DealResult GetBasicInfo(string name) { var user = new User() { Name = name, Id = CurrentUserId }; return DealResult(true, string.Empty, user); } /// /// 测试异常 /// ///
[HttpGet] public DealResult TestException() { int a = 0; var b = 10 / a; return DealResult(true); } }
View Code

 

此控制器标注了SignCheckAttribute用以进行签名判断

具体实现可看SignCheckAttribute代码

SignCheckAttribute里实现了有

  • 数据签名判断
  • 签名超时判断
  • 用户登录限制
  • 签名重复使用处理(一个签名只能使用一次)
  • 过期登录用户处理(没有主动退出用户清理)

为了统一处理异常,配置了异常处理

1
GlobalConfiguration.Configuration.Filters.Add(
new
ExceptionAttribute());

对接口进行测试

大杀器来了,配合此方案放出了对应的测试工具,虽然WEB API有个扩展,但没法对此方案测试

使用此工具能方便按方案要求调用接口,为了方便参数拼接,POST和GET都采用URL参数的形式输入

测试登录/api/account/login

测试获取信息/api/account/GetBasicInfo

测试异常处理/api/account/TestException

在未登录情况下调用获取信息

接口文档

接口结构文档一直是很让人头疼的事,手写更改了又得维护,版本不一样还麻烦,自动生成最好了,同样WEB API 带扩展没法表示此结构详细

大杀器2号来了,按代码注释动态生成接口文档,文档格式与控制器保持一致

Home控制器代码实现

public ActionResult Index(SummaryAnalysis.ExportType exportType = SummaryAnalysis.ExportType.NONE)        {            if (exportType != SummaryAnalysis.ExportType.NONE)            {                var str = SummaryAnalysis.Load(exportType); return File(str, "application/octet-stream", "Model_" + exportType + ".zip"); } else { if (string.IsNullOrEmpty(outPut)) { outPut = SummaryAnalysis.Load(exportType); } ViewBag.OutPut = outPut; return View(); } } }

在见过的开发文档,我觉得这是最好的展现形式了,还有锚点,快速定位到对象结构,并且与源代码保持一致

附WEB API 自带文档生成区别

附上项目源码

http://pan.baidu.com/s/1c2rDacK

项目结构:

         ----------WPF测试程序

         ----------接口示例

虽然跟CRL快速开发框架无关,但还是加上CRL的名,好文要顶!


参与讨论,QQ群:1582632 密语 CRL

 

相关推荐

转载于:https://www.cnblogs.com/MuNet/p/6688396.html

你可能感兴趣的文章
BZOJ5072:[Lydsy1710月赛]小A的树(树形DP)
查看>>
单链表及其算法演示(Data structure course from HaoBin)
查看>>
Day6-Dhcp
查看>>
NOIP2015 运输计划 二分答案+Tarjan LCA+树上差分
查看>>
构建之法读后感
查看>>
hdu题型分类
查看>>
08.LoT.UI 前后台通用框架分解系列之——多样的Tag选择器
查看>>
Bootstrap3基础 btn-xs/sm... 按钮的四种大小
查看>>
silverlight 乐动魔方 实战九 .
查看>>
Learn Python 016: Coin - a project of msc, unfinished.
查看>>
Python爬虫(一)
查看>>
霍夫变换的基本理解(第八天)
查看>>
解决TensorFlow程序无限制占用GPU
查看>>
[转] vue从入门到进阶:组件Component详解(六)
查看>>
C/C++预定义宏
查看>>
正则表达式(二)
查看>>
OpenCV (iOS)中的形态学变换(11)
查看>>
关于ONION/Wncry勒索病毒
查看>>
我是一个iOS程序员
查看>>
七个你可能不了解的CSS单位
查看>>