單位網站建設工作功勞網絡營銷策劃書包括哪些內容
需求場景
由于項目需要開發(fā)第三方接口給多個供應商,為保證Api接口的安全性,遂采用Api接口簽名驗證。
Api接口簽名驗證主要防御措施為以下幾個:
- 請求發(fā)起時間得在限制范圍內
- 請求的用戶是否真實存在
- 是否存在重復請求
- 請求參數(shù)是否被篡改
項目路徑
https://gitee.com/charles_ruan/easy-sign
代碼實現(xiàn)
不同的客戶端有著不同的appSecret
。
- 通過密鑰可以為
不同的客戶端(調用方) 分配不同的appSecret
,來區(qū)分不同客戶端app(調用方)。 - 將獲取到的appSecret 參與到
sign(簽名)
的生成,保證了客戶端的請求簽名是由我們后臺控制的。
定義切面,攔截帶SignatureValidation
方法。
- 獲取方法上的參數(shù),存入
SortedMap
- 判斷參數(shù)是否合法
- 判斷appId是否存在對應的secret
- 判斷時間戳是否有效
- 進行簽名校驗
@Slf4j
@Aspect
public class SignAspect {@Before("@annotation(signatureValidation)")public void doBefore(SignatureValidation signatureValidation) throws Throwable {// 接收到請求,記錄請求內容HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();SortedMap<String, String> allParams = HttpUtils.getAllParams(request);// 1、獲取請求sign簽名參數(shù),String sign = allParams.get("sign");if (StrUtil.isBlank(sign)) {throw new RuntimeException("sign不能為空");}// 2、獲取請求參數(shù)secretString appId = allParams.get("appId");String appSecret = getAppSecret(appId);if (StrUtil.isBlank(appSecret)) {throw new RuntimeException("appId不合法");}// 3、獲取請求參數(shù)timestamp 時間戳,String timestamp = allParams.get("timestamp");if (StrUtil.isBlank(timestamp)) {throw new RuntimeException("timestamp不能為空");}//3. 比較時間,120s內為合法請求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) {// 對方簽名String sign = allParams.get("sign");allParams.remove("sign");String mySign = SecureUtil.md5(JSONUtil.toJsonStr(allParams)).toUpperCase();log.info("驗簽,對方簽名:{},我方簽名:{}", sign, mySign);// 驗簽Assert.isTrue(StrUtil.equals(sign, mySign), "驗簽失敗");}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ù),可以防止重復提交,在簽名驗證成功后,判斷是否重復提交,原理就是結合redis,判斷是否已經提交過
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;}
標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符 標題復制10行,并且每行大于10個字符