java使用拦截器进行接口签名验证
之前有使用aop自定义注解的方式;
本篇签名验证都一样;时效;防抓包重复请求;redis验签
拦截器方法感觉解耦更强吧,结合全局异常(之前文章里有-)
1.签名验证拦截器 @Component @Slf4j public class SignAuthInterceptor implements HandlerInterceptor { private final static Long REDIS_SIGN_EXPIRE_TIME = 5 * 60 * 1000L; private final static String REDIS_KEY_PREFIX = "APPID:"; private final static String REDIS_APPSIGN_PREFIX = "APPSIGN:FILESERVER:";
@Autowired StringRedisTemplate redisTemplate;
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String timestamp = request.getHeader("timestamp"); String appid = request.getHeader("appid"); String sign = request.getHeader("sign"); String method = request.getMethod();
if (StringUtils.isBlank(appid) || StringUtils.isBlank(sign) || StringUtils.isBlank(timestamp)) { returnJson(response, ReturnCode.INVALID_HEADER); return false; }
// 验证时间是否有效 long now = System.currentTimeMillis(); if (Math.abs(now - Long.valueOf(timestamp)) >= REDIS_SIGN_EXPIRE_TIME) { returnJson(response, ReturnCode.INVALID_TIMESTAMP); return false; }
// 验证sign MD5(appid+appkey+timestamp) 此处,省事 默认secret为Md5(appId).substring(10,14) String appSecret = ""; if (StringUtils.isBlank(appSecret)) { appSecret = DigestUtils.md5DigestAsHex(appid.getBytes(StandardCharsets.UTF_8)).substring(10, 14); } String md5Str = appid + appSecret + timestamp; log.info(md5Str); String encode = DigestUtils.md5DigestAsHex(md5Str.getBytes(StandardCharsets.UTF_8)); log.info(encode);
if (!StringUtils.equals(sign, encode) || redisTemplate.hasKey(REDIS_APPSIGN_PREFIX + sign)) { returnJson(response, ReturnCode.INVALID_SIGN); return false; }
redisTemplate.opsForValue() .set(REDIS_APPSIGN_PREFIX + sign, "1", REDIS_SIGN_EXPIRE_TIME, TimeUnit.MILLISECONDS);
return HandlerInterceptor.super.preHandle(request, response, handler); }
// 异常返回值;可以用全局异常替代,之前的文章里有 // ReturnCode 为枚举返回值 private void returnJson(HttpServletResponse response, ReturnCode returnCode){ PrintWriter writer = null; response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); try { writer = response.getWriter(); writer.print(JSON.toJSONString(ResponseWrapper.error(returnCode))); } catch (IOException e){ log.error(e.getMessage()); } finally { if(writer != null){ writer.close(); } } } }
2.添加全局配置 @Configuration public class SignAuthWebConfig implements WebMvcConfigurer {
@Autowired private SignAuthInterceptor signAuthInterceptor;
@Override public void addInterceptors(InterceptorRegistry registry) { // 添加拦截规则 registry.addInterceptor(signAuthInterceptor).addPathPatterns("/test/**"); }
// 若对参数验签,此处添加过滤器配置;防止接口层不能获取RequestBody值 // @Bean // public FilterRegistrationBean registrationBean() { // FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter()); // filterRegistrationBean.addUrlPatterns("/*"); // // return filterRegistrationBean; // }
}