接口参数签名校验,是WebApi接口服务最重要的安全防护手段之一. 结合项目中实际使用情况,介绍下前后端参数签名校验实现方案。

签名校验规则

http请求,有两种传参形式:

1.通过url传参,最常见的就是get请求(实际上post,put,delete都可以使用这种传参方式),如:

http://api.XXX.com/getproduct?id=value1

2.通过request body传参,最常见的就是post请求,如下图所示



我们针对于以上两种传参方式,采用不同的签名校验规则(注:签名算法规则仅供参考)。WebApi是不支持通过url和body同时传参数的,所以在服务端可以通过HttpContext.Current.Request.QueryString 获取到form参数进行判断,执行不同逻辑,如下代码所示:

var form = HttpContext.Current.Request.QueryString; // 请求的url参数
var data = string.Empty;
if (form.Count > 0)
{
//第一步:取出所有form参数
IDictionary<string, string> parameters = new Dictionary<string, string>();
for (var f = 0; f < form.Count; f++)
{
var key = form.Keys[f];
parameters.Add(key, form[key]);
} // 第二步:把字典按Key的字母顺序排序
IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
var dem = sortedParams.GetEnumerator(); // 第三步:把所有参数名和参数值串在一起
var query = new StringBuilder();
while (dem.MoveNext())
{
var key = dem.Current.Key;
var value = dem.Current.Value;
if (!string.IsNullOrEmpty(key)) query.Append(key).Append(value);
}
data = query.ToString();
}
else
{
//请求输入的内容,即body内容
var stream = HttpContext.Current.Request.InputStream;
stream.Position = 0;
var responseJson = string.Empty;
var streamReader = new StreamReader(stream);
data = streamReader.ReadToEnd();
stream.Position = 0;
}

通过上述逻辑之后,data变量中存储的就是接口参数内容。 以下是实际签名校验逻辑

/// <summary>
/// 签名校验
/// </summary>
/// <param name="timeStamp">时间戳(按秒)</param>
/// <param name="data">参数内容</param>
/// <param name="signature">前端签名值</param>
/// <returns></returns>
public bool Validate(string timeStamp, string data, string signature)
{
var hash = MD5.Create();
//拼接签名数据
var signStr = timeStamp + data;
//将字符串中字符按升序排序
var sortStr = string.Concat(signStr.OrderBy(c => c));
var bytes = Encoding.UTF8.GetBytes(sortStr);
//使用32位大写 MD5签名
var md5Val = hash.ComputeHash(bytes);
var result = new StringBuilder();
foreach (var c in md5Val) result.Append(c.ToString("X2"));
var s = result.ToString().ToUpper();
//与前端传过来的签名参数进行比对
return s == signature;
}

Action拦截器实现对某些Api进行签名校验

创建WebApi的Action拦截器HandlerSecretAttribute

/// <summary>
/// 签名安全拦截过滤器
/// </summary>
public class HandlerSecretAttribute : ActionFilterAttribute
{
private readonly ExcuteMode _customMode; /// <summary>默认构造</summary>
/// <param name="Mode">认证模式</param>
public HandlerSecretAttribute(ExcuteMode Mode)
{
_customMode = Mode;
} /// <summary>
/// 安全校验
/// </summary>
/// <param name="filterContext"></param>
public override void OnActionExecuting(HttpActionContext filterContext)
{
//是否忽略权限验证
if (_customMode == ExcuteMode.Ignore) return; //从http请求的头里面获取AppId
var request = filterContext.Request;
var method = request.Method.Method;
var appId = ""; //客户端应用唯一标识
long timeStamp; //时间戳, 校验10分钟内有效
var signature = ""; //参数签名,去除空参数,按字母倒序排序进行Md5签名 为了提高传参过程中,防止参数被恶意修改,在请求接口的时候加上sign可以有效防止参数被篡改
try
{
appId = request.Headers.GetValues("appId").SingleOrDefault();
timeStamp = Convert.ToInt64(request.Headers.GetValues("timeStamp").SingleOrDefault());
signature = request.Headers.GetValues("signature").SingleOrDefault();
}
catch (Exception ex)
{
throw new UserFriendlyException("签名参数异常:" + ex.Message);
} #region 安全校验
//TODO:实际逻辑处理
base.OnActionExecuting(filterContext);
#endregion
}
}

具体的使用,如下图所示:

如果是需要全局注册,请在WebApi.config中配置,如下图所示:

有关HandlerSecretAttribute的源代码,我已整理放至github上https://github.com/yinboxie/BlogExampleDemo

前端Axios请求统一拦截处理

客户端http请求(以Axios为例)进行统一的拦截处理。前端用过诸多http插件,如ajax,fetch,vue-resoure,axios等,个人感觉axios的请求拦截是最好用的。

import axios from 'axios'
import { sign } from './sign'
let _ = require('lodash')
var service = axios.create({
baseURL:'http://xxx.com'
timeout:10000 // 请求超时时间
}) // request拦截器
service.interceptors.request.use(
config => {
let token = getToken()
if (token) {
config.headers['token'] = token // 让每个请求携带自定义token 请根据实际情况自行修改
}
// 如果接口需要签名, 则通过请求时,headers中传递sign参数true
if (config.headers['sign']) {
config = sign(config) // 核心签名逻辑,独立封装了处理函数
}
return config
},
error => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)

sign函数核心源码

import md5 from 'js-md5'
let _ = require('lodash')
/**
* 接口参数签名
* @param {*} config 请求配置
*/
export const sign = config => {
// 获取到秒级的时间戳,与后端对应
let tmp = new Date()
.getTime()
.toString()
.substr(0, 10)
let header = {
appId:'pmes',
timeStamp: tmp,
signature: ''
}
let signStr = _.toString(header.timeStamp)
if (config.params) {
// url参数签名
let pArray = []
for (let p in config.params) {
pArray.push(p)
}
let sArray = pArray.sort()
for (let item of sArray) {
signStr += item + _.toString(config.params[item])
}
} else if (config.data) {
// request body参数的内容
signStr += JSON.stringify(config.data)
} // 签名核心逻辑
let newsignStr = _.sortBy(signStr, s => s.charCodeAt(0)).join('')
let s = md5(newsignStr).toUpperCase()
header.signature = s
config = Object.assign(config, { headers: header })
return config
}

实际的调用函数

// post请求, body传参
axios.post('/Login/CheckLoginTest1',
{ Account: 'xiaowang',Password: '123' },
{ headers: { sign: true }}
).then(d => {
console.log(d)
})
// post请求,url传参
axios.post('/Login/CheckLoginTest3',
null,
{
params: {t1: '2',t2: '3'},
headers: { sign: true }
}
).then(d => {
console.log(d)
})
// get请求
axios.get('/Login/CheckLoginTest2',
{
params: {t1: '2',t2: '3'},
headers: { sign: true }
}
).then(d => {
console.log(d)
})

总结

为了保证WebApi数据在通信时的安全性,需要采取多重安全防护: 参数签名校验,token验证,跨域权限,时间戳过期校验,允许请求的appId校验等。当然具体的规则我们都得依据项目实际情况去实现,这里就不再展开讨论其他方式的实现。

参考

WebApi安全性 使用TOKEN+签名验证

WebApi安全性 参数签名校验(结合Axios使用)的更多相关文章

  1. [置顶] webapi token、参数签名是如何生成的

    一个问题 在这里我想问大家一句,如果你向一个刚刚接触.net web后端程序开发的同学(别人刚刚也就学了webform的request,response,会提交表单的这种刚接触不久的同学),你怎么去解 ...

  2. webapi token、参数签名是如何生成的(转载)

    API接口保障安全性原则:1.有调用者身份2.请求的唯一性3.请求的参数不能被篡改4.请求的有效时间 在刚接触接口开发时,可能脑子里压根就没有这个接口调用安全性的原则,但常识性的经验告诉我们,每一个请 ...

  3. webAPI过滤器添加参数签名

    项目需求: 接口对安卓和IOS开发接口,需要房子用户窜改数据请求接口.添加sign签名校验参数. 代码如下:加上特性标签就可以控制部分接口验证 public class SignAuthorizeFi ...

  4. WebApi安全性 使用TOKEN+签名验证

    首先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候,会面临着许多的安全性问题, ...

  5. WebAPI 安全性 使用TOKEN+签名验证(上)

    首先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候,会面临着许多的安全性问题, ...

  6. WebApi安全性 使用TOKEN+签名验证 (秘钥是GUID的,私有的,不是雙方的,并不在网络连接上传输)

    转http://www.cnblogs.com/MR-YY/archive/2016/10/18/5972380.html WebApi安全性 使用TOKEN+签名验证   首先问大家一个问题,你在写 ...

  7. 接口鉴权之sign签名校验与JWT验证

    需求描述: 项目里的几个Webapi接口需要进行鉴权,同接口可被小程序或网页调用,小程序里没有用户登录的概念,网页里有用户登录的概念,对于调用方来源是小程序的情况下进行放权,其他情况下需要有身份验证. ...

  8. Apk去签名校验详解

    某些apk为了防止重打包,使用了签名校验.所以在破解的时候我们需要破解签名校验.在定位签名校验位置时常用的关键词有sign,signature,checkSign,signCheck,getPacka ...

  9. 利用Spring AOP自定义注解解决日志和签名校验

    转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...

随机推荐

  1. 基于 EntityFramework 生成 Repository 模式代码

    借助 WeihanLi.EntityFramework 实现简单的 Repository Intro 很多时候一些简单的业务都是简单的增删改查,动态生成一些代码完成基本的增删改查,而这些增删改查代码大 ...

  2. 死磕 java同步系列之ReentrantReadWriteLock源码解析

    问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...

  3. C# - VS2019通过重写pictureBox实现简单的桌面截图功能

    前言 通过创建客制化组件(继承pictureBox),新增属性和构造方法,实现屏幕截图时需要用到的功能点.再通过监控鼠标按下.移动和释放,来获取起始点区域.最后通过操作BMP图像,实现截图的新增.修改 ...

  4. 教你如何用Python向手机发送通知

    ------------恢复内容开始------------ 你曾想尝试在服务器端或电脑上向手机发送通知吗? 你曾烦恼过企业邮箱的防骚扰机制吗? 现在,我们可以用一种简单轻松的方法来代替企业邮箱了! ...

  5. 漫谈golang设计模式 工厂模式

    工厂模式 意义:创建过程交给专门的工厂子类去完成.定义一个抽象的工厂类,再定义具体的工厂类来生成子类等,它们实现在抽象按钮工厂类中定义的方法.这种抽象化的结果使这种结构可以在不修改具体工厂类的情况下引 ...

  6. 网站的favicon图标

    网站的favicon图标 favicon.ico一般用于作为缩略的网站标志,它显示在浏览器的地址栏或者标签上. 制作favicon图标 把图片转换为png图片 把png图片转换为ico图标,这需要借助 ...

  7. CSS符合选择器

    CSS复合选择器 后代选择器 后代选择器又称为包含选择器,可以选择父元素里面的子元素.其写法就是把外层标签写在前面,内层标签写在后面,中间用空格分隔,当标签发生嵌套时,内层标签就成为外层标签的后代 元 ...

  8. BayaiM__经典SQL语句大全

    BayaiM__经典SQL语句大全 原创 作者:bayaim 时间:2016-06-16 09:22:24 32 0删除编辑 以下这篇文章,是一个垃圾,垃圾中的垃圾 ----------------- ...

  9. react相关小技巧

    一.我们在项目中切换路由的时候可能会遇到 Warning: setState(...): Can only update a mounted or mounting component. This u ...

  10. 字符设备驱动程序之poll机制(韦大仙)

    明确为什么要引用poll机制? while(1) { read(fd,&key_val,1);//如果没有按键按下,它会一直在等待.现在想做这么一件事情:如果5s后,没有按键按下,它就会返回. ...