对外界提供的接口,需要加签名保证安全,签名可以用md5,sha256都行,本文是md5
签名文档
比如:
appId:testffhz
appSecret:rv30GsgvPxxSH23L
代码
拦截器:
/** * 对外接口签名验签 * * @author lihaoyang * @date 2022/2/22 */ @Slf4j @Component @Order(-9) public class SignVerifyInterceptor extends HandlerInterceptorAdapter { @Autowired private OpenApiUsersProperties openApiUsersProperties; @Autowired private RedisUtil redisUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } MyRequestWrapper requestWrapper = new MyRequestWrapper(request); String bodyJson = new String(requestWrapper.getBody(),Charset.forName("UTF-8")); log.info("signVerifyInterceptor bodyJson:{}",bodyJson); HandlerMethod handlerMethod = (HandlerMethod) handler; SkSign openApiSignAnnotation = handlerMethod.getMethodAnnotation(SkSign.class); if (Objects.isNull(openApiSignAnnotation)) { return true; } String appId = requestWrapper.getHeader(SignConstant.APP_ID); String sign = requestWrapper.getHeader(SignConstant.SIGNATURE_STR); String timestamp = requestWrapper.getHeader(SignConstant.TIME_STAMP); String nonce = requestWrapper.getHeader(SignConstant.NONCE_STR); log.info("signverify request param,appId:{},sign:{},timestamp:{},nonce:{}", appId, sign, timestamp, nonce); if (StringUtils.isBlank(appId)) { log.info("signverify:appId is blank "); response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.LOST_PARAM_APPID)); return false; } if (StringUtils.isBlank(sign)) { log.info("signverify:sign is blank "); response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.LOST_PARAM_SIGN)); return false; } if (StringUtils.isBlank(timestamp)) { log.info("signverify:timestamp is blank "); response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.LOST_PARAM_TIME_STAMP)); return false; } if (StringUtils.isBlank(nonce)) { log.info("signverify:nonce is blank "); response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.LOST_PARAM_NONCE)); return false; } DateTime reqDateTime = null; try { reqDateTime = DateUtil.parse(timestamp, DatePattern.NORM_DATETIME_PATTERN); } catch (Exception e) { log.warn("signverify ,解析timestamp失败,入参 timestamp:{}",timestamp); response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.TIMESTAMP_FMT_ERROR)); return false; } if (DateUtil.between(reqDateTime.toJdkDate(), new Date(), DateUnit.SECOND) > SignConstant.expireTimeSeconds) { //超时 log.warn("signverify ,sign过期,超时时间(秒数):{}", SignConstant.expireTimeSeconds); response(response,Result.error(ResultEnum.OPEN_API_TIMEOUT.getCode(), SignConstant.OUT_OF_DATE_REQUEST)); return false; } Long nonceCounter = redisUtil.incr(RedisKeyUtil.getNonceKey(nonce),1); if (nonceCounter > 1) { log.info("signverify ,重复调用,nonceCounter:{}",nonceCounter); //重复调用 response(response,Result.error(ResultEnum.OPEN_API_REPEAT_CALL)); return false; } else { log.info("signverify ,nonce 首次调用设置过期时间,nonce:{}",nonce); redisUtil.expire(RedisKeyUtil.getNonceKey(nonce), SignConstant.expireTimeSeconds + 10); } String signStr = appId + getAppSecret(appId) + bodyJson + timestamp + nonce; String sign2 = DigestUtils.md5Hex(signStr).toUpperCase(); log.info("signverify get sign >>>>>>:{}" + signStr); if (!StringUtils.equals(sign2, sign)) { response(response, Result.error(ResultEnum.SIGN_AUTH_FAIL)); return false; } return true; } private String getAppSecret(String appId) { return openApiUsersProperties.getAppMap().get(appId); } public static void response(HttpServletResponse response, Result<?> result) throws IOException { response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().write(JSONUtil.toJsonStr(result)); response.getWriter().flush(); } }
WebMVC配置
/** * @author lihaoyang * @date 2022/5/27 */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private SignVerifyInterceptor signVerifyInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { /*****************/ registry.addInterceptor(signVerifyInterceptor); } }
配置类,这里配置到了Apollo,可以配置在表,都行
/** * 对外对接 * @author lihaoyang * @date 2022/2/25 */ @Slf4j @Configuration @ConfigurationProperties(prefix = "openapi") public class OpenApiUsersProperties { private Map<String, String> appMap = new HashMap(); public Map<String, String> getAppMap() { return appMap; } public void setAppMap(Map<String, String> appMap) { this.appMap = appMap; } }
签名注解:
/** * 对外接口签名 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface SkSign { }
Request包装类和过滤器,不加这个json的参数在拦截器读一次controller就读不到了
/** * @author lihaoyang * @date 2022/5/30 */ @Slf4j @Component @WebFilter(urlPatterns = "/*", filterName = "requestWrapperFilter") public class RequestWrapperFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { log.info("enter requestWrapperFilter doFilter"); HttpServletRequest httpServletRequest = (HttpServletRequest) request; MyRequestWrapper requestWrapper = new MyRequestWrapper(httpServletRequest); chain.doFilter(requestWrapper,response); } }
public class MyRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public MyRequestWrapper(HttpServletRequest request) throws IOException { super(request); body = getBodyString(request).getBytes(Charset.forName("UTF-8")); } @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new InputStreamReader(getInputStream())); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body); return new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } @Override public boolean isFinished() { return false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener readListener) { } }; } /** * 获取请求Body * * @param request * @return */ private String getBodyString(ServletRequest request) { StringBuilder sb = new StringBuilder(); InputStream inputStream = null; BufferedReader reader = null; try { inputStream = request.getInputStream(); reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8"))); String line = ""; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } public byte[] getBody() { return body; } }
Controller
@SkSign @PostMapping("pageList") public Result pageList(@RequestBody TeamListReq req){ req.initParam(); Page<TeamListResp> page = teamService.findPageList(req); return Result.OK(page); }
请求:
标签:return,String,接口,response,签名,验签,new,SignConstant,public From: https://www.cnblogs.com/lihaoyang/p/16857451.html