一般的项目 如果没有做防刷 容易被人爆接口 或者就是说没有做token防刷过滤。
容易被人用正常的token刷接口。有些token非一次性。
用户登录之后生成token会有一个过期时间,但一般没有做频率检查,每访问一次,会延长这个token时间,刷新用户状态
另一种就是养号,拿着真实的token,哪怕你是5分钟 1分钟。
很多的网站找回密码的接口是没有做防刷的,只是检查token是否正常。
通过验证码认证当前用户,是否为当前用户。
前几天,就用多线程刷过一个三方网站的找回密码。成功改掉密码。
一般的网站在改密码的接口都会先查一次此号码是否已经注册,相反就可以通过这个接口猜出真实的用户手机号,
然后多线程调这个接口猜验证码,一般为4位,复杂点的为6位。也会有一些项目加了图形拖拽(第三方)
前端会提交相关信息给第三方平台,分析你是不是正常的用户动作,直接封IP。
但是用户体验差一点,有些网站为了用户体验,忽略了网站安全性,看业务上的取舍了。
进入正题:简易版(demo)
aop 实现 :
package com.zhouixi.serviceA.aspect; import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.swing.text.Keymap; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import com.zhouixi.serviceA.annotation.LimitKey; @Component @Order @Aspect public class LimitAspect { private Map limitMap = new HashMap(); private static final Logger log = LoggerFactory.getLogger(LimitAspect.class); @Pointcut("@annotation(limitKey)") public void limit(LimitKey limitKey) { } @Around("limit(limitKey)") public Object aroundLog(ProceedingJoinPoint joinpoint,LimitKey limitKey) throws Throwable { MethodSignature methodSignature = (MethodSignature) joinpoint.getSignature(); int frequency = limitKey.frequency(); String methodName = limitKey.methodName(); String paramKey = limitKey.paramKey(); String url = limitKey.url(); //入参 String[] parameterNames = methodSignature.getParameterNames(); Object[] args = joinpoint.getArgs(); Object obj = null ; for(int i = 0 ; i < parameterNames.length;i++) { if(parameterNames[i].equals(paramKey)) { obj = args[i]; break; } } System.err.println("args : "+Arrays.toString(args)); System.err.println("keys : "+Arrays.toString(parameterNames)); StringBuffer sb = new StringBuffer(); sb.append(url).append("/_").append(methodName).append("_").append(paramKey).append("_").append(obj).append("_key"); if(limitMap.get(sb.toString()) == null ) { limitMap.put(sb.toString(),frequency-1); } else { int l = (int) limitMap.get(sb.toString()); if(l > 0){ limitMap.put(sb.toString(), --l); } else { throw new Exception ("系统繁忙,请重试"); } } //reids 代替map redis.set(sb.toString(),frequency,limitKey.timeout()); System.err.println("剩余次数:"+limitMap.get(sb.toString())+"----->----->----->自定义key:"+sb.toString()); return joinpoint.proceed(); } }
controller:
注解声明:
效果图:
最后一次:
postman:
完成。真实生产场景要换成redis 其他nosql