單位網(wǎng)站建設(shè)工作功勞網(wǎng)絡(luò)營銷策劃書包括哪些內(nèi)容
需求場(chǎng)景
由于項(xiàng)目需要開發(fā)第三方接口給多個(gè)供應(yīng)商,為保證Api接口的安全性,遂采用Api接口簽名驗(yàn)證。
Api接口簽名驗(yàn)證主要防御措施為以下幾個(gè):
- 請(qǐng)求發(fā)起時(shí)間得在限制范圍內(nèi)
- 請(qǐng)求的用戶是否真實(shí)存在
- 是否存在重復(fù)請(qǐng)求
- 請(qǐng)求參數(shù)是否被篡改
項(xiàng)目路徑
https://gitee.com/charles_ruan/easy-sign
代碼實(shí)現(xiàn)
不同的客戶端有著不同的appSecret
。
- 通過密鑰可以為
不同的客戶端(調(diào)用方) 分配不同的appSecret
,來區(qū)分不同客戶端app(調(diào)用方)。 - 將獲取到的appSecret 參與到
sign(簽名)
的生成,保證了客戶端的請(qǐng)求簽名是由我們后臺(tái)控制的。
定義切面,攔截帶SignatureValidation
方法。
- 獲取方法上的參數(shù),存入
SortedMap
- 判斷參數(shù)是否合法
- 判斷appId是否存在對(duì)應(yīng)的secret
- 判斷時(shí)間戳是否有效
- 進(jìn)行簽名校驗(yàn)
@Slf4j
@Aspect
public class SignAspect {@Before("@annotation(signatureValidation)")public void doBefore(SignatureValidation signatureValidation) throws Throwable {// 接收到請(qǐng)求,記錄請(qǐng)求內(nèi)容HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();SortedMap<String, String> allParams = HttpUtils.getAllParams(request);// 1、獲取請(qǐng)求sign簽名參數(shù),String sign = allParams.get("sign");if (StrUtil.isBlank(sign)) {throw new RuntimeException("sign不能為空");}// 2、獲取請(qǐng)求參數(shù)secretString appId = allParams.get("appId");String appSecret = getAppSecret(appId);if (StrUtil.isBlank(appSecret)) {throw new RuntimeException("appId不合法");}// 3、獲取請(qǐng)求參數(shù)timestamp 時(shí)間戳,String timestamp = allParams.get("timestamp");if (StrUtil.isBlank(timestamp)) {throw new RuntimeException("timestamp不能為空");}//3. 比較時(shí)間,120s內(nèi)為合法請(qǐng)求if (Math.abs(Long.parseLong(timestamp) - System.currentTimeMillis()) > 120000) {throw new RuntimeException("timestamp失效");}allParams.put("secret", appSecret);verifySign(allParams);}private void verifySign(SortedMap<String, String> allParams) {// 對(duì)方簽名String sign = allParams.get("sign");allParams.remove("sign");String mySign = SecureUtil.md5(JSONUtil.toJsonStr(allParams)).toUpperCase();log.info("驗(yàn)簽,對(duì)方簽名:{},我方簽名:{}", sign, mySign);// 驗(yàn)簽Assert.isTrue(StrUtil.equals(sign, mySign), "驗(yàn)簽失敗");}public String getAppSecret(String appId) {Map<String, String> map = new HashMap<>();map.put("zs001", "asd123fhg3b7fgh7dfg");map.put("ls001", "hghfgh123btgfyh1212");return map.get(appId);}
}
利用nonce參數(shù),可以防止重復(fù)提交,在簽名驗(yàn)證成功后,判斷是否重復(fù)提交,原理就是結(jié)合redis,判斷是否已經(jīng)提交過
public boolean isReplayAttack(String appId, String timeStamp, String nonce, String signature) {StringBuilder redisKey = new StringBuilder();redisKey.append("IS_REPLAY_ATTACK").append(":").append(Constant.APP_ID).append(":").append(appId).append(Constant.TIME_STAMP).append(":").append(timeStamp).append(Constant.NONCE).append(":").append(nonce).append(Constant.SIGN).append(":").append(signature);Object value = redisTemplate.opsForValue().get(redisKey);if (value != null && StringUtils.equals(signature, value.toString()))return false;elseredisTemplate.opsForValue().set(redisKey, signature, 1000 * 50);return false;}
標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符 標(biāo)題復(fù)制10行,并且每行大于10個(gè)字符