SpringBoot 如何保证接口安全?( 三 )

 @Slf4j public class SignFilter implements Filter { @Resource private RedisUtil redisUtil; ? //从fitler配置中获取sign过期时间 private Long signMaxTime; ? private static final String NONCE_KEY = "x-nonce-"; ? @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; ? log.info("过滤URL:{}", httpRequest.getRequestURI()); ? HttpServletRequestWrapper requestWrapper = new SignRequestWrapper(httpRequest); //构建请求头 RequestHeader requestHeader = RequestHeader.builder() .nonce(httpRequest.getHeader("x-Nonce")) .timestamp(Long.parseLong(httpRequest.getHeader("X-Time"))) .sign(httpRequest.getHeader("X-Sign")) .build(); ? //验证请求头是否存在 if(StringUtils.isEmpty(requestHeader.getSign()) || ObjectUtils.isEmpty(requestHeader.getTimestamp()) || StringUtils.isEmpty(requestHeader.getNonce())){ responseFail(httpResponse, ReturnCode.ILLEGAL_HEADER); return; } ? /* * 1.重放验证 * 判断timestamp时间戳与当前时间是否操过60s(过期时间根据业务情况设置),如果超过了就提示签名过期 。*/ long now = System.currentTimeMillis() / 1000; ? if (now - requestHeader.getTimestamp() > signMaxTime) { responseFail(httpResponse,ReturnCode.REPLAY_ERROR); return; } ? //2. 判断nonce boolean nonceExists = redisUtil.hasKey(NONCE_KEY + requestHeader.getNonce()); if(nonceExists){ //请求重复 responseFail(httpResponse,ReturnCode.REPLAY_ERROR); return; }else { redisUtil.set(NONCE_KEY+requestHeader.getNonce(), requestHeader.getNonce(), signMaxTime); } ? ? boolean accept; SortedMap paramMap; switch (httpRequest.getMethod()){ case "GET": paramMap = HttpDataUtil.getUrlParams(requestWrapper); accept = SignUtil.verifySign(paramMap, requestHeader); break; case "POST": paramMap = HttpDataUtil.getBodyParams(requestWrapper); accept = SignUtil.verifySign(paramMap, requestHeader); break; default: accept = true; break; } if (accept) { filterChain.doFilter(requestWrapper, servletResponse); } else { responseFail(httpResponse,ReturnCode.ARGUMENT_ERROR); return; } ? } ? private void responseFail(HttpServletResponse httpResponse, ReturnCode returnCode) { ResultData resultData = https://www.isolves.com/it/cxkf/kj/2023-02-03/ResultData.fail(returnCode.getCode(), returnCode.getMessage()); WebUtils.writeJson(httpResponse,resultData); } ? @Override public void init(FilterConfig filterConfig) throws ServletException { String signTime = filterConfig.getInitParameter("signMaxTime"); signMaxTime = Long.parseLong(signTime); } }
6、Redis工具类
@Component public class RedisUtil { @Resource private RedisTemplate redisTemplate; ? /** * 判断key是否存在 * @param key 键 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return Boolean.TRUE.equals(redisTemplate.hasKey(key)); } catch (Exception e) { e.printStackTrace(); return false; } } ? ? /** * 普通缓存放入并设置时间 * @param key 键 * @param value 值 * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 * @return true成功 false 失败 */ public boolean set(String key, Object value, long time) { try { if (time > 0) { redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); } else { set(key, value); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } ? /** * 普通缓存放入 * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key, Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } ? }

作者:飘渺Jam 链接:https://juejin.cn/post/7195355046065176631 来源:稀土掘金




推荐阅读