前后端分离的开发方式,我们以接口为标准来进行推动,定义好接口,各自开发自己的功能,最后进行联调整合。无论是开发原生的APP还是webapp还是PC端的软件,只要是前后端分离的模式,就避免不了调用后端提供的接口来进行业务交互。
网页或者app,只要抓下包就可以清楚的知道这个请求获取到的数据,也可以伪造请求去获取或攻击服务器。先考虑一下接口数据被伪造,以及接口被重复调用的问题,要解决这个问题我们就要用到接口签名的方案
签名流程:

参数说明
关键参数 | 参数名 | 参数作用 | 说明 |
source_system | 系统来源 | 用于区分不同客户端来源。并根据不同的客户端来源获取source_secret用于生产signature签名 | 前后端按照约定线下分配 |
request_id | 请求流水号 | 用于判断是否重复请求 | 随机生产,唯一性 |
request_time | 请求时间 | 用于请求连接过期,让链接有失效时间 | 单位毫秒 |
access_token | 令牌 | 权限验证 | 采用Spring Security OAuth 2.0生成 |
signature | 签名 | 用于比较参数是否篡改 | 不可逆加密方式 |
签名规则:
1、线下分配source_system和source_secret,针对不同的调用方分配不同的source_system和source_secret
2、加入request_time 作为请求的时间戳,5分钟内数据有效
3、加入临时流水号request_id(防止重复提交),每次请求生成的随机字符串。同时流水号也用于日志落地,便于后期日志核查。流水号存在有效期、唯一性,以避免重复请求。
4、加入签名字段signature,所有数据的签名信息。
以上字段放在请求头中
签名的生成:
签名signature字段生成规则
所有动态参数 = 请求头部分 + 请求URL地址 + 请求Request参数 + 请求Body
上面的动态参数以key-value的格式存储,并以key值正序排序,进行拼接
最后拼接的字符串 在拼接source_secret。
signature =DigestUtils.md5DigestAsHex(sortParamsMap +source_secret)
即拼接成一个字符串,然后做md5不可逆加密
签名算法实现:
自定义filter过滤器,对每个请求进行拦截处理;整体流程如下
1)验证必须的头部参数
2)获取头部参数,Request参数,Url请求路径,请求体Body,把这些值放入SortMap中进行排序
3)对SortMap里面的值进行拼接
4)对拼接的值进行加密,生成signature
5)把生成的signature和前端传入的signature进行比较,如果不相同就返回错误
代码实现:

以上是filter过滤器,其中有个RequestWrapper继承HttpServletRequestWrapper来获取请求流(请求流只能获取一次,参数后续需要进行校验,因此暂存了请求流中的请求信息)
验证头部参数:


上图其实就是验证是否传入值;不过其实有个很重要的一点,就是对此请求进行时间验证,如果大于10分钟表示此链接已经超时,防止别人来到这个链接去请求。这个就是防止盗链。
如何获取各个参数:

上面我们获取了各个参数,并把参数信息放到了一个以key值正序排序的SortMap中
生成signature签名,并验证和前端传入的signature进行比较



将暂存的请求流中的参数信息放入到SortMap中,根据自己的规则对SortMap里面的值进行拼接,即拼接成一个字符串并转换成小写,然后做md5不可逆加密。
重放攻击处理方案:
虽然解决了请求参数被篡改的隐患,但是还存在着重复使用请求参数伪造二次请求的隐患。
request_time +request_id方案
request_id指唯一的随机字符串,用来标识每个被签名的请求。通过为每个请求提供一个唯一的标识符,服务器能够防止请求被多次使用(记录所有用过的request_id以阻止它们被二次使用)。
然而,对服务器来说永久存储所有接收到的request_id的代价是非常大的。可以使用request_time 来优化request_id的存储。
假设允许客户端和服务端最多能存在15分钟的时间差,同时追踪记录在服务端的request_id集合。当有新的请求进入时,首先检查携带的request_time 是否在15分钟内,如超出时间范围,则拒绝,然后查询携带的request_id,如存在已有集合,则拒绝。否则,记录该request_id,并删除集合内时间戳大于15分钟的request_id(使用redis的expire,新增request_id的同时设置它的超时失效时间为15分钟)。
重放攻击处理方案
防止盗链,我们可以让链接有失效时间
利用流水号request_id参数,防止重复提交
在签名验证成功后,判断是否重复提交;
原理就是结合redis,判断是否已经提交过


END







