接口参数签名校验,是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. elasticsearch window下配置安装

    1.首先下载elasticsearch 下载链接:https://www.elastic.co/cn/downloads/elasticsearch 第一张图是下载的,第二章图是下载msi的程序,直接 ...

  2. Javase之多线程(1)

    多线程(1) 多线程的概述 了解多线程之前需要先了解线程,而线程依赖于进程而存在,所以先了解进程. 什么是进程 进程就是正在运行的程序.是系统进行资源分配和调用的独立单位.每一个进程都有它自己的内存空 ...

  3. PeriscopeHeartAnimation

    // // ViewController.m // PeriscopeHeartAnimation // // Created by ldj on 4/28/15. // Copyright (c) ...

  4. Java并发编程艺术读书笔记

    1.多线程在CPU切换过程中,由于需要保存线程之前状态和加载新线程状态,成为上下文切换,上下文切换会造成消耗系统内存.所以,可合理控制线程数量. 如何控制: (1)使用ps -ef|grep appn ...

  5. mac上安装npm

    检查brew -v是否安装了homebrew这个macOS 缺失的软件包的管理器.如果安装,跳转到第3步,否则跳转到第二步: 安装homebrew.安装跳转到官网指导.等待安装好之后,输入brew - ...

  6. emacs 矩形操作

    emacs 矩形操作 如果使用图形化(GUI)的eamcs,使用M-x cua-mode,很好用,但是如果不是图形化的emacs(emacs -nw)的话,矩形操作就不能使用cua-mode. 非图形 ...

  7. java链接集合

    Intellij IDEA 导入eclipse web 项目详细操作 https://blog.csdn.net/deng11408205/article/details/79723213?utm_s ...

  8. 2019徐州网络赛 H.function

    题意: 先有\(n=p_1^{k_1}p_2^{k_2}\cdots p_m^{k_m}\),定义\(f(n)=k_1+k_2+\cdots+k_m\). 现在计算 \[ \sum_{i=1}^nf( ...

  9. 201871010113-刘兴瑞《面向对象程序设计(java)》第四周学习总结

    项目 内容 这个作业属于哪个课程 <任课教师博客主页链接>https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 <作业链接地址>http ...

  10. Session中短信验证码设置有效时间

    Session中短信验证码设置有效时间 package com.mozq.boot.kuayu01.controller; import org.springframework.web.bind.an ...