一 公共参数安全性
访问接口 需要传指定参数 才能进行访问
@Component public class SystemParamsFilter implements Filter { @Autowired private StringRedisTemplate stringRedisTemplate; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; if (req.getRequestURL().toString().contains("login")) { chain.doFilter(request, response); return; } //获取set不重复的 的公共参数 Set<String> members = stringRedisTemplate.opsForSet().members(JWtPrex.SYSTEM_PARM); if (members != null) { for (String param : members) { //公共参数的值 String paramValue = request.getParameter(param); if (StrUtil.isEmpty(paramValue)) { JsonResultBean baseResultBean = new JsonResultBean(); baseResultBean.setCode("-200"); baseResultBean.setMsg(TimeStampException.SYSTEM_TIME_ERROR +param); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(new ObjectMapper().writeValueAsString(baseResultBean)); return; } else { continue; } } } chain.doFilter(request, response); } config中 @Bean public FilterRegistrationBean SystemParamsFilter(SystemParamsFilter systemParamsFilter) { //配置类对象 FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); //配置对象 filterRegistrationBean.setFilter(systemParamsFilter); //设置名字 filterRegistrationBean.setName("systemParamsFilter"); //设置顺序,正整数值越小,优先级越高 filterRegistrationBean.setOrder(3); //设置拦截路径 filterRegistrationBean.addUrlPatterns("/*"); return filterRegistrationBean; }
二 签名 和验签
完整的是要公钥和密钥
演示 根据你传的参数来md5的方式 生成sign 通过用户来验证
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; if (req.getRequestURL().toString().contains("login")){ chain.doFilter(request,response); return; } //获取参数的名字根据迭代器来放到map集合中 Enumeration<String> parameterNames = request.getParameterNames(); HashMap<String, String> map = new HashMap<>(); while (parameterNames.hasMoreElements()){ String elementName = parameterNames.nextElement(); if (!"sign".equalsIgnoreCase(elementName)){ String parameValue = request.getParameter(elementName); map.put(elementName,parameValue); } } String secret="888888"; //md5加密 String md5Signature = Md5Util.md5Signature(map, secret).trim(); System.out.println(md5Signature); String UserSign = request.getParameter("sign").trim(); if (md5Signature.equals(UserSign)){ chain.doFilter(request,response); }else { JsonResultBean baseResultBean = new JsonResultBean(); baseResultBean.setCode("-666"); baseResultBean.setMsg("请传sign"); //返回数据 response.setContentType("application/json;charset=utf-8"); response.getWriter().write(new ObjectMapper().writeValueAsString(baseResultBean)); } }
md5工具包
private static String byte2hex(byte[] b) { StringBuffer hs = new StringBuffer(); String stmp = ""; for (int n = 0; n < b.length; n++) { stmp = (Integer.toHexString(b[n] & 0XFF)); if (stmp.length() == 1) { hs.append("0").append(stmp); } else { hs.append(stmp); } } return hs.toString().toUpperCase(); } /*** * 对请求的参数排序,生成定长的签名 * @param paramsMap 排序后的字符串 * @param secret 密钥 * */ public static String md5Signature(Map<String, String> paramsMap, String secret) { String result = ""; StringBuilder sb = new StringBuilder(); Map<String, String> treeMap = new TreeMap<String, String>(); treeMap.putAll(paramsMap); sb.append(secret); Iterator<String> iterator = treeMap.keySet().iterator(); while (iterator.hasNext()) { String name = (String) iterator.next(); sb.append(name).append(treeMap.get(name)); } sb.append(secret); try { MessageDigest md = MessageDigest.getInstance("MD5"); /**MD5加密,输出一个定长信息摘要*/ result = byte2hex(md.digest(sb.toString().getBytes("utf-8"))); } catch (Exception e) { throw new RuntimeException("sign error !"); } return result; } /** * Calculates the MD5 digest and returns the value as a 16 element * <code>byte[]</code>. * * @param data Data to digest * @return MD5 digest */ public static byte[] md5(String data) { return md5(data.getBytes()); } /** * Calculates the MD5 digest and returns the value as a 16 element * <code>byte[]</code>. * * @param data Data to digest * @return MD5 digest */ public static byte[] md5(byte[] data) { return getDigest().digest(data); } /** * Returns a MessageDigest for the given <code>algorithm</code>. * * @param * @return An MD5 digest instance. * @throws RuntimeException when a {@link NoSuchAlgorithmException} is * caught */ static MessageDigest getDigest() { try { return MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } }
三幂等性概念
幂等性原本是数学上的概念,用在接口上就可以理解为:同一个接口,多次发出同一个请求,必须保证操作只执行一次。
调用接口发生异常并且重复尝试时,总是会造成系统所无法承受的损失,所以必须阻止这种现象的发生。
比如下面这些情况,如果没有实现接口幂等性会有很严重的后果:
支付接口,重复支付会导致多次扣钱
订单接口,同一个订单可能会多次创建。
(8条消息) 什么是接口的幂等性,如何实现接口幂等性?一文搞定_Java鱼仔的博客-CSDN博客_接口的幂等性是什么意思
解决方式唯一索引
使用唯一索引可以避免脏数据的添加,当插入重复数据时数据库会抛异常,保证了数据的唯一性。
@Autowired private StringRedisTemplate stringRedisTemplate; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; if (req.getRequestURL().toString().contains("login")) { chain.doFilter(request, response); return; } //获取当前Api接口的是否需要保证幂等性 //假设:idempotent = 1 则需要保证该接口幂等性 //通过Api接口的key拿到idempotent的value String idempotent = stringRedisTemplate.opsForHash().get(JWtPrex.SYSTEM_IDEMPOTENT +"findAll", "idempotent").toString(); System.out.println(idempotent); if ("1;".equals(idempotent)) { String sign = request.getParameter("sign"); String signIdempotent = stringRedisTemplate.opsForValue().get(JWtPrex.SYSTEM_IDEMPOTENT + sign); System.out.println(signIdempotent); if (signIdempotent.isEmpty()) { stringRedisTemplate.opsForValue().set(JWtPrex.SYSTEM_IDEMPOTENT + sign, sign, 60000); chain.doFilter(request,response); return; }else { JsonResultBean baseResultBean = new JsonResultBean(); baseResultBean.setCode("9999"); baseResultBean.setMsg("不能重复访问"); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(new ObjectMapper().writeValueAsString(baseResultBean)); } } else { chain.doFilter(request, response); } }
config 过滤器
FilterRegistrationBean 对象