大家好,欢迎来到IT知识分享网。
一、业务背景
接口开发是各系统之间对接的重要方式,其数据是通过开放的互联网传输,对数据的安全性要有一定要求。为了提高传输过程参数的防篡改性,签名sign的方式是目前比较常用的方式
二、技术实施方案
- 鉴权方案:
1.公共鉴权参数(所有接口必须带有的参数,参数支持存放在HTTP headers【优先级高】或者url链接参数上)
2.签名生成规则
secret= (秘钥,内部协定)
①所有业务接口服务 请求方式均为 POST, 请求参数类型Content-Type=application/json
②sign 签名字符生成规则为 MD5( secret+client + format +time + version+ RequstBody(请求参数对象).toJSONString() + secret).toLowerCase()
3.签名流程
- 客户端:按上述要求生成签名sign,并连同上述参数存入请求headers
- 服务端:验证流程(timeout默认时长15分钟)
- 技术方案:后端使用过滤器进行统一接口鉴权
@Slf4j public class ApiSignAuthFilter implements Filter { /** * 秘钥 */ @Value private String secret; /** * 签名参数名称集合 */ private static String[] params = new String[]{"client", "cuid","format", "time", "version", "sign"}; private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher(); @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; //特殊场景,可以跳过验证(具体看业务) if (!StrUtil.equalsIgnoreCase(request.getMethod(), HttpMethod.POST.name()) || !StrUtil.containsIgnoreCase(request.getHeader(HttpHeaders.CONTENT_TYPE),MediaType.APPLICATION_JSON_VALUE)) { chain.doFilter(req, res); }else { //api参数签名校验 try { chain.doFilter(verifySign(request), res); }catch (BusinessException e) { HttpServletResponse response = (HttpServletResponse) res; response.setContentType("application/json;charset=UTF-8"); String originalURL = request.getHeader("Origin"); if (originalURL != null) { response.addHeader("Access-Control-Allow-Origin", originalURL); } response.addHeader("Access-Control-Allow-Credentials", "true"); PrintWriter out = response.getWriter(); out.append(JSONHelper.toJSONString(ResponseBean.error(e.getErrorCode(),e.getMessage()))); } } } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } /*** * 签名校验 * @param request */ private HttpServletRequestWrapper verifySign(HttpServletRequest request) { String client = getRequestParam(request, params[0]); // 客户端类型 if (StringUtil.isEmpty(cuid) && "wx".equals(client)) { cuid = getIpAddr(request); } String format = getRequestParam(request, params[2]); // 请求格式 json String time = getRequestParam(request, params[3]); // 请求时间 String version = getRequestParam(request, params[4]); // 应用版本号 String sign = getRequestParam(request, params[5]); // 验证签名 if (StringUtil.isEmpty(client) || StringUtil.isEmpty(version) || StringUtil.isEmpty(cuid) || StringUtil.isEmpty(format) || StringUtil.isEmpty(time) || StringUtil.isEmpty(sign)) { throw new BusinessException(BizExceptionEnum.SIGN_ERROR_1.getResponseCode(),BizExceptionEnum.SIGN_ERROR_1.getResponseMsg()); } if(DateUtil.between(new Date(Convert.toLong(time,0l)), new Date(), DateUnit.MINUTE) > 15) { throw new BusinessException(BizExceptionEnum.SIGN_ERROR_3.getResponseCode(),BizExceptionEnum.SIGN_ERROR_3.getResponseMsg()); } String bodyJsonStr = StrUtil.EMPTY; BodyReaderHttpServletRequestWrapper requestWrapper = null; if (!request.getRequestURI().contains("upload")) { try { requestWrapper = new BodyReaderHttpServletRequestWrapper(request); bodyJsonStr = requestWrapper.getRequestPostStr(requestWrapper); if(StrUtil.isBlank(bodyJsonStr)) { bodyJsonStr = StrUtil.EMPTY; } } catch (IOException e) { log.error("解析请求body异常",e); throw new BusinessException(BizExceptionEnum.SIGN_ERROR_2.getResponseCode(),BizExceptionEnum.SIGN_ERROR_2.getResponseMsg()); } } //拼接签名 StringBuilder signSource = new StringBuilder(); signSource.append(secret).append(client).append(cuid).append(format).append(time).append(version) .append(bodyJsonStr).append(secret); String keySign = MD5Implementor.MD5Encode(signSource.toString()).toLowerCase(); if (!StrUtil.equals(keySign,sign)) { log.error("接口{}签名验证失败,请求签名串:{},sign:{}",request.getRequestURL(), signSource, sign); throw new BusinessException(BizExceptionEnum.SIGN_ERROR.getResponseCode(),BizExceptionEnum.SIGN_ERROR.getResponseMsg()); } return requestWrapper; } /** * 从request中获取参数,先重head获取没有再从请求url上的参数中获取 * @param request * @param key * @return */ private String getRequestParam(HttpServletRequest request, String key) { String value = request.getHeader(key); if(StrUtil.isBlank(value)) { value = request.getParameter(key); } return value; } }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/49933.html