WebApi安全性参数签名校验(结合Axios使用)

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

签名校验规则

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

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

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

2.通过request body传参,最常见的就是post请求,如下图所示
webapi参数签名-01
我们针对于以上两种传参方式,采用不同的签名校验规则(注:签名算法规则仅供参考)。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参数签名-02

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

webapi参数签名-03

有关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+签名验证

原文链接:https://www.cnblogs.com/xyb0226/p/11048342.html

原创文章,作者:优速盾-小U,如若转载,请注明出处:https://www.cdnb.net/bbs/archives/18514

(0)
上一篇 2023年8月3日
下一篇 2023年8月3日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

优速盾注册领取大礼包www.cdnb.net
/sitemap.xml