概述
与前端对接的API接口,如果被第三方抓包并进行歹意篡改参数,可能会导致数据泄露,甚至会被篡改数据,我次要围绕工夫戳,token,签名三个局部来保障API接口的安全性
1.用户胜利登陆站点后,服务器会返回一个token,用户的任何操作都必须带了这个参数,能够将这个参数间接放到header里。
2.客户端用须要发送的参数和token生成一个签名sign,作为参数一起发送给服务端,服务端在用同样的办法生成sign进行查看是否被篡改。
3.但这仍然存在问题,可能会被进行歹意无限度拜访,这时咱们须要引入一个工夫戳参数,如果超时即是有效的。
4.服务端须要对token,签名,工夫戳进行验证,只有token无效,工夫戳未超时,签名无效能力被放行。
凋谢接口
没有进行任何限度,简略粗犷的拜访形式,这样的接口方式个别在凋谢的利用平台,查天气,查快递,只有你输出正确对应的参数调用,即可获取到本人须要的信息,咱们能够任意批改参数值。
/* * Description: 凋谢的接口 * @author huangweicheng * @date 2020/12/21*/@RestController@RequestMapping("/token")public class TokenSignController { @Autowired private TokenSignService tokenSignService; @RequestMapping(value = "openDemo",method = RequestMethod.GET) public List<PersonEntity> openDemo(int personId){ return tokenSignService.getPersonList(personId); }}
Token认证获取
用户登录胜利后,会获取一个ticket值,接下去任何接口的拜访都须要这个参数。咱们把它搁置在redis内,有效期为10分钟,在ticket行将超时,无感知续命。缩短应用工夫,如果用户在一段时间内没进行任何操作,就须要从新登录零碎。扩大:记一次token平安认证的实际
@RequestMapping(value = "login",method = RequestMethod.POST) public JSONObject login(@NotNull String username, @NotNull String password){ return tokenSignService.login(username,password); }
登录操作,查看是否有这个用户,用户名和明码匹配即可胜利登录。
/** * * Description:验证登录,ticket胜利后搁置缓存中, * @param * @author huangweicheng * @date 2020/12/31 */ public JSONObject login(String username,String password){ JSONObject result = new JSONObject(); PersonEntity personEntity = personDao.findByLoginName(username); if (personEntity == null || (personEntity != null && !personEntity.getPassword().equals(password))){ result.put("success",false); result.put("ticket",""); result.put("code","999"); result.put("message","用户名和明码不匹配"); return result; } if (personEntity.getLoginName().equals(username) && personEntity.getPassword().equals(password)){ String ticket = UUID.randomUUID().toString(); ticket = ticket.replace("-",""); redisTemplate.opsForValue().set(ticket,personEntity.getLoginName(),10L, TimeUnit.MINUTES); result.put("success",true); result.put("ticket",ticket); result.put("code",200); result.put("message","登录胜利"); return result; } result.put("success",false); result.put("ticket",""); result.put("code","1000"); result.put("message","未知异样,请重试"); return result; }
Sign签名
把所有的参数拼接一起,在退出零碎秘钥,进行MD5计算生成一个sign签名,避免参数被人歹意篡改,后盾按同样的办法生成秘钥,进行签名比照。
/** * @param request * @return */ public static Boolean checkSign(HttpServletRequest request,String sign){ Boolean flag= false; //查看sigin是否过期 Enumeration<?> pNames = request.getParameterNames(); Map<String, String> params = new HashMap<String, String>(); while (pNames.hasMoreElements()) { String pName = (String) pNames.nextElement(); if("sign".equals(pName)) continue; String pValue = (String)request.getParameter(pName); params.put(pName, pValue); } System.out.println("当初的sign-->>" + sign); System.out.println("验证的sign-->>" + getSign(params,secretKeyOfWxh)); if(sign.equals(getSign(params, secretKeyOfWxh))){ flag = true; } return flag; }
反复拜访
引入一个工夫戳参数,保障接口仅在一分钟内无效,须要和客户端工夫保持一致。
public static long getTimestamp(){ long timestampLong = System.currentTimeMillis(); long timestampsStr = timestampLong / 1000; return timestampsStr; }
须要跟以后服务器工夫进行比照,如果超过一分钟,就回绝本次申请,节俭服务器查问数据的耗费
拦截器
每次申请都带有这三个参数,咱们都须要进行验证,只有在三个参数都满足咱们的要求,才容许数据返回或被操作。
public class LoginInterceptor implements HandlerInterceptor { @Autowired private RedisTemplate redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws IOException { JSONObject jsonObject = new JSONObject(); String ticket = request.getParameter("ticket"); String sign = request.getParameter("sign"); String ts = request.getParameter("ts"); if (StringUtils.isEmpty(ticket) || StringUtils.isEmpty(sign) || StringUtils.isEmpty(ts)){ jsonObject.put("success",false); jsonObject.put("message","args is isEmpty"); jsonObject.put("code","1001"); PrintWriter printWriter = response.getWriter(); printWriter.write(jsonObject.toJSONString()); return false; } //如果redis存在ticket就认为是非法的申请 if (redisTemplate.hasKey(ticket)){ System.out.println(redisTemplate.opsForValue().getOperations().getExpire(ticket)); String values = (String) redisTemplate.opsForValue().get(ticket); //判断ticket是否行将过期,进行续命操作 if (redisTemplate.opsForValue().getOperations().getExpire(ticket) != -2 && redisTemplate.opsForValue().getOperations().getExpire(ticket) < 20){ redisTemplate.opsForValue().set(ticket,values,10L, TimeUnit.MINUTES); } System.out.println(SignUtils.getTimestamp()); //判断是否反复拜访,存在重放攻打的工夫窗口期 if (SignUtils.getTimestamp() - Long.valueOf(ts) > 600){ jsonObject.put("success",false); jsonObject.put("message","Overtime to connect to server"); jsonObject.put("code","1002"); PrintWriter printWriter = response.getWriter(); printWriter.write(jsonObject.toJSONString()); return false; } //验证签名 if (!SignUtils.checkSign(request,sign)){ jsonObject.put("success",false); jsonObject.put("message","sign is invalid"); jsonObject.put("code","1003"); PrintWriter printWriter = response.getWriter(); printWriter.write(jsonObject.toJSONString()); return false; } return true; }else { jsonObject.put("success",false); jsonObject.put("message","ticket is invalid,Relogin."); jsonObject.put("code","1004"); PrintWriter printWriter = response.getWriter(); printWriter.write(jsonObject.toJSONString()); } return false; }}
拜访
先登录零碎,获取非法的ticket
生成一个非法的sign验证,获取测试ts,拜访openDemo,即可失常拜访。还能够将参数加密,将http换成https,就不一 一开展了。
demo代码
https://github.com/hwc4110/sp...
作者:一剑天门
cnblogs.com/dslx/p/14116294.html