接口参数签名校验,是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. go-百度贴吧-纵向爬取

    百度贴吧纵向爬取 上一个是横向爬取的,这个纵向爬取,具体怎么做的看代码 package main import ( "fmt" "io" "net/h ...

  2. flex布局开发

    flex布局开发 布局原理 flex时flexible Box的缩写,意为"弹性布局",用来为盒子模型提供最大的灵活性,任何一个容器都可以定位flex布局 [注意] 当我们为父盒子 ...

  3. 【转载】Gradle for Android 第六篇( 测试)

    由于现阶段Android开发趋于敏捷开发,再加上国内大大小小的互联网公司都在做app,导致很多这会是一个系列,所以如果你看完这篇文章,请看下列文章: 开发人员对单元测试没有基本的概念,但是本篇博文不会 ...

  4. STM32 IAP 升级功能

    IAP In Application Programming 可通过USB,CAN,UART,I2C,SPI等接口实现 IAP流程 Bootloader程序:接收升级程序,更新到flash指定地址:跳 ...

  5. Linux system v 共享内存

    system v 共享内存 #include <sys/types.h> #include <sys/shm.h> int shmget(key_t key, size_t s ...

  6. Matplotlib 日期格式转换

    官网链接:https://matplotlib.org/api/dates_api.html#matplotlib.dates.date2num import numpy as np import d ...

  7. vue--实现跑马灯效果

    <div id="app"> <input type="button" value="开始" @click="l ...

  8. 高频Python面试题分享

    一.Python语言中你用过哪些方式来实现进程间通信1.队列Queue 2.Pipe管道 只适用于两个进程之间的通信, pipe的效率高于queue 3.共享内存 4.socket套接字(UDP即可) ...

  9. day69_10_14 drf接口框架。

    一.drf框架简介 drf全程是:django-rest framework. 其中涉及的知识点有. 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful规范下 ...

  10. 如何对jmeter设置IP欺骗

    由于服务器出于安全考虑会对同一IP地址做过滤,所以如果想要达到正常的压测效果,我们需要在发请求时伪造出不同的IP地址.主要步骤分为以下3步:第一步:在负载机上绑定IP地址. 第二步:在要欺骗的http ...