访问权限控制

方法一、自定义注解

利用spring切面或拦截器实现;

可以对所有接口或添加注解的接口实现请求次数限制;

利用缓存的过期时间功能,uri太长可以做个映射(为缓存节省空间);

ip要防止利用代理访问;

限制维度:ip级别、用户+接口级别;

*******************************注解类

[java] view plain copy

 

1.   /** 

2.    * request请求次数限制 

3.    * 示例:@RequestLimit(ipCount=2,ipTime=60000,uriCount=2,uriTime=60000) 

4.    * @date 2016-03-23 13:28:06 

5.    */  

6.   @Target(ElementType.METHOD)  

7.   @Retention(RetentionPolicy.RUNTIME)  

8.   @Inherited  

9.   @Documented  

10.  public @interface RequestLimit {  

11.    

12.      /** 

13.       * ip允许访问的次数,默认值1000 

14.       */  

15.      int ipCount() default 1000;  

16.    

17.      /** 

18.       * ip时间段,单位为毫秒,默认值一分钟 

19.       */  

20.      long ipTime() default 60000;  

21.        

22.      /** 

23.       * uri允许访问的次数,默认值600 

24.       */  

25.      int uriCount() default 600;  

26.    

27.      /** 

28.       * uri时间段,单位为毫秒,默认值一分钟 

29.       */  

30.      long uriTime() default 60000;  

31.    

32.  }  

*******************************

*******************************spring
切面

[java] view plain copy

 

1.   public Object around(ProceedingJoinPoint pjp)  {  

2.   //拦截的实体类  

3.       Object target = pjp.getTarget();  

4.       //拦截的方法名称  

5.       String methodName = pjp.getSignature().getName();  

6.       //拦截的放参数类型  

7.       Class[] parameterTypes = ((MethodSignature)pjp.getSignature()).getMethod().getParameterTypes();  

8.       Class[] clazzs = target.getClass().getInterfaces();  

9.       //1.获取类  

10.      Class clazz = target.getClass();  

11.      if (clazzs != null && clazzs.length > 0){  

12.          clazz = clazzs[0];  

13.      }  

14.      //2.获取方法  

15.      Method m = clazz.getMethod(methodName, parameterTypes);  

16.      //3.获取requestcallback  

17.      Object[] args = pjp.getArgs();  

18.      HttpServletRequest request = null;  

19.      if (args != null && args.length > 0) {  

20.          if (args[0instanceof HttpServletRequest) {  

21.              request = (HttpServletRequest) args[0];  

22.              if(request != null){  

23.                  callback = request.getParameter("callback");  

24.              }  

25.          }  

26.      }  

27.      //RequestLimit判断  

28.      String reequestLimitRes = this.RequestLimitCheck(m, request);  

29.      if("fail".equals(reequestLimitRes)){  

30.          return "fail";//返回值改为自己的格式  

31.      }else{  

32.          Object obj = pjp.proceed();  

33.      }  

34.  }  

35.    

36.  private String RequestLimitCheck(Method m, HttpServletRequest request) throws IOException{  

37.      //ipuser_phone+uri  两个维度的访问限制  

38.      if(m!=null && m.isAnnotationPresent(RequestLimit.class)){  

39.          RequestLimit requestLimit = m.getAnnotation(RequestLimit.class);  

40.          //失效时间、访问次数  

41.          int ipTime = (int) (requestLimit.ipTime() / 1000);  

42.          int ipCount = requestLimit.ipCount();  

43.          int uriTime = (int) (requestLimit.uriTime() / 1000);  

44.          int uriCount = requestLimit.uriCount();  

45.    

46.          //ipuser_phoneuri  

47.          String ip_key = NetworkUtil.getIpAddress(request);//ip:获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址;  

48.          String user_phone = request.getParameter("user_phone");//手机号  

49.          String uri = request.getRequestURI().toString();//uri:如果字符串太长,可以做个映射  

50.          String user_uri_key = user_phone + uri;  

51.          //先从缓存中(Memcached为例)获取两个key对应的value,value值为访问限制次数  

52.          List<String> keyCollections = new ArrayList<String>();  

53.          keyCollections.add(ip_key);  

54.              keyCollections.add(user_uri_key);  

55.          Map<String, String> valueMap = memcachedClient.get(keyCollections);//一次读取出多个key_value  

56.          Integer ipNumCache = 0;//ip访问次数  

57.              Integer userUriNumCache = 0;//手机号+uri访问次数  

58.              if(valueMap!=null && valueMap.size()>0){  

59.                  String ipNumCacheFlag = valueMap.get(ip_key);  

60.                  if(StringUtils.isNotBlank(ipNumCacheFlag)){  

61.                      ipNumCache = Integer.parseInt(ipNumCacheFlag);  

62.                  }  

63.                  String userUriNumCacheFlag = valueMap.get(user_uri_key);  

64.                  if(StringUtils.isNotBlank(userUriNumCacheFlag)){  

65.                      userUriNumCache = Integer.parseInt(userUriNumCacheFlag);  

66.                  }  

67.              }  

68.          //ip限制判断  

69.              if(ipNumCache == 0){  

70.                  memcachedClient.set(ip_key, 1, ipTime);  

71.              }else if(ipNumCache >= ipCount){  

72.                  logger.info("request_limit:用户IP[" + ip_key + "],超过了限定的次数[" + ipCount + "]");  

73.                  return "fail";  

74.              }else{  

75.                  memcachedClient.incr(ip_key, 1);//自增  

76.              }  

77.          //user_phoneuri限制判断  

78.          if(userUriNumCache == 0){  

79.              memcachedClient.set(user_uri_key, 1, uriTime);  

80.          }else if(userUriNumCache >= uriCount){  

81.              logger.info("request_limit:用户手机号[" + user_phone + "],访问地址[" + uri + "],超过了限定的次数[" + uriCount + "]");  

82.              return "fail";  

83.          }else{  

84.              memcachedClient.incr(user_uri_key, 1);  

85.          }  

86.      }  

87.      return "success";  

88.  }  

*******************************
后续:

可以将不正常调用的用户添加进黑名单,保存在数据库中...

爬虫项目会定期定时访问接口,后台分析...

 

 

 

 

 

 

 


方法二、使用缓存实现

数据访问量大的话 用redis来做,用户在调用短信接口时,先根据用户id去查一下次数,如果没有id这个key,证明就是1分钟内首次发送,发送后,在redis中记录一个key为id的次数为一次,在为这个key加上过期时间1分钟

 


 

方法三、过滤器、监听器

工程启动时,创建两个Map,一个(ipMap)用来存放用户Ip和访问时间等主要信息,另一个(limitedIpMap)用来存放被限制的用户IPMapkey为用户的IPvalue为具体内容。

当用户访问系统时,通过IPFilter检查limitedIpMap中是否存在当前IP,如果存在说明该IP之前存在过恶意刷新访问,已经被限制,跳转到异常提示页面;如果limitedIpMap

中不存在则检查ipMap中是否存在当前IP,如果ipMap中不存在则说明用户初次访问,用户访问次数+1,初始访问时间为当前时间;如果存在则检查用户访问次数是否在规定的短时间内进行了大量的访问操作;如果是,则将当前IP添加到limitedIpMap中,并跳转到异常提示页面,否则不进行操作,直接放行本次请求。

配置文件:

 

Xml代码  

1. <!-- 配置过滤器 start -->  

2. <filter>  

3.     <filter-name>IPFilter</filter-name>  

4.     <filter-class>com.test.interceptor.IPFilter</filter-class>  

5. </filter>  

6. <filter-mapping>  

7.     <filter-name>IPFilter</filter-name>  

8.     <url-pattern>/render/*</url-pattern>  

9. </filter-mapping>  

10.<!-- 配置过滤器 end -->  

11.      

12.<!-- 配置监听器 start -->  

13.<listener>  

14.    <listener-class>com.test.listener.MyListener</listener-class>  

15.</listener>  

16.<!-- 配置监听器 start -->  

 

监听器MyListener:

 

Java代码  

1.  import java.util.concurrent.ConcurrentHashMap;  

2.    

3.  import javax.servlet.ServletContext;  

4.  import javax.servlet.ServletContextEvent;  

5.  import javax.servlet.ServletContextListener;  

6.    

7.  /** 

8.   * @Description 自定义监听器,项目启动时初始化两个全局的ConcurrentHashMap(线程安全) 

9.   *              ipMap(ip存储器,记录IP的访问次数、访问时间) 

10.  *              limitedIpMap(限制IP存储器)用来存储每个访问用户的IP以及访问的次数 

11.  * @author zhangyd 

12.  * @date 2016728 下午5:47:23 

13.  * @since JDK  1.7 

14.  * @version 2.0 

15.  * @modify hashMap  ConcurrentHashMap 

16.  */  

17. public class MyListener implements ServletContextListener {  

18.   

19.     @Override  

20.     public void contextInitialized(ServletContextEvent sce) {  

21.         ServletContext context = sce.getServletContext();  

22.         // IP存储器  

23.         ConcurrentHashMap<String, Long[]> ipMap = new ConcurrentHashMap<String, Long[]>();  

24.         context.setAttribute("ipMap", ipMap);  

25.         // 限制IP存储器:存储被限制的IP信息  

26.         ConcurrentHashMap<String, Long> limitedIpMap = new ConcurrentHashMap<String, Long>();  

27.         context.setAttribute("limitedIpMap", limitedIpMap);  

28.     }  

29.   

30.     @Override  

31.     public void contextDestroyed(ServletContextEvent sce) {  

32.   

33.     }  

34.   

35. }  

 

过滤器IPFilter:

 

Java代码  

1.  import java.io.IOException;  

2.  import java.util.concurrent.ConcurrentHashMap;  

3.    

4.  import javax.servlet.Filter;  

5.  import javax.servlet.FilterChain;  

6.  import javax.servlet.FilterConfig;  

7.  import javax.servlet.ServletContext;  

8.  import javax.servlet.ServletException;  

9.  import javax.servlet.ServletRequest;  

10. import javax.servlet.ServletResponse;  

11. import javax.servlet.http.HttpServletRequest;  

12. import javax.servlet.http.HttpServletResponse;  

13.   

14. import com.test.util.IPUtil;  

15.   

16. /** 

17.  *  

18.  * @Description 自定义过滤器,用来判断IP访问次数是否超限。<br> 

19.  *              如果前台用户访问网站的频率过快(比如:达到或超过50/s),则判定该IP为恶意刷新操作,限制该ip访问<br> 

20.  *              默认限制访问时间为1小时,一小时后自定解除限制 

21.  *  

22.  * @author zhangyd 

23.  * @date 2016728 下午5:54:51 

24.  * @since JDK  1.7 

25.  * @version 2.0 

26.  * @modify hashMap  线程安全的ConcurrentHashMap 

27.  */  

28. public class IPFilter implements Filter {  

29.   

30.     /** 默认限制时间(单位:ms */  

31.     private static final long LIMITED_TIME_MILLIS = 60 * 1000;  

32.   

33.     /** 用户连续访问最高阀值,超过该值则认定为恶意操作的IP,进行限制 */  

34.     private static final int LIMIT_NUMBER = 20;  

35.   

36.     /** 用户访问最小安全时间,在该时间内如果访问次数大于阀值,则记录为恶意IP,否则视为正常访问 */  

37.     private static final int MIN_SAFE_TIME = 5000;  

38.   

39.     private FilterConfig config;  

40.   

41.     @Override  

42.     public void init(FilterConfig filterConfig) throws ServletException {  

43.         this.config = filterConfig;  

44.     }  

45.   

46.     /** 

47.      * @Description 核心处理代码 

48.      * @param servletRequest 

49.      * @param servletResponse 

50.      * @param chain 

51.      * @throws IOException 

52.      * @throws ServletException 

53.      * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, 

54.      *      javax.servlet.ServletResponse, javax.servlet.FilterChain) 

55.      */  

56.     @SuppressWarnings("unchecked")  

57.     @Override  

58.     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)  

59.             throws IOException, ServletException {  

60.         HttpServletRequest request = (HttpServletRequest) servletRequest;  

61.         HttpServletResponse response = (HttpServletResponse) servletResponse;  

62.   

63.         ServletContext context = config.getServletContext();  

64.         // 获取限制IP存储器:存储被限制的IP信息  

65.         ConcurrentHashMap<String, Long> limitedIpMap = (ConcurrentHashMap<String, Long>) context  

66.                 .getAttribute("limitedIpMap");  

67.         // 获取用户IP  

68.         String ip = IPUtil.getIp(request);  

69.         // 判断是否是被限制的IP,如果是则跳到异常页面  

70.         if (isLimitedIP(limitedIpMap, ip)) {  

71.             long limitedTime = limitedIpMap.get(ip) - System.currentTimeMillis();  

72.             forward(request, response, ((limitedTime / 1000) + (limitedTime % 1000 > 0 ? 1 : 0)));  

73.             return;  

74.         }  

75.         // 获取IP存储器  

76.         ConcurrentHashMap<String, Long[]> ipMap = (ConcurrentHashMap<String, Long[]>) context.getAttribute("ipMap");  

77.   

78.         // 判断存储器中是否存在当前IP,如果没有则为初次访问,初始化该ip  

79.         // 如果存在当前ip,则验证当前ip的访问次数  

80.         // 如果大于限制阀值,判断达到阀值的时间,如果不大于[用户访问最小安全时间]则视为恶意访问,跳转到异常页面  

81.         if (ipMap.containsKey(ip)) {  

82.             Long[] ipInfo = ipMap.get(ip);  

83.             ipInfo[0] = ipInfo[0] + 1;  

84.             if (ipInfo[0] > LIMIT_NUMBER) {  

85.                 Long ipAccessTime = ipInfo[1];  

86.                 Long currentTimeMillis = System.currentTimeMillis();  

87.                 // 限制时间内  

88.                 if (currentTimeMillis - ipAccessTime <= MIN_SAFE_TIME) {  

89.                     System.out  

90.                             .println(ip + [" + (currentTimeMillis - ipAccessTime) + "]ms内,共访问了[" + ipInfo[0] + "]");  

91.                     limitedIpMap.put(ip, currentTimeMillis + LIMITED_TIME_MILLIS);  

92.                     forward(request, response, currentTimeMillis + LIMITED_TIME_MILLIS);  

93.                     return;  

94.                 } else {  

95.                     initIpVisitsNumber(ipMap, ip);  

96.                 }  

97.             }  

98.         } else {  

99.             initIpVisitsNumber(ipMap, ip);  

100.          }  

101.          context.setAttribute("ipMap", ipMap);  

102.          chain.doFilter(request, response);  

103.      }  

104.    

105.      @Override  

106.      public void destroy() {  

107.    

108.      }  

109.    

110.      /** 

111.       * @Description 跳转页面 

112.       * @author zhangyd 

113.       * @date 2016817 下午5:58:43 

114.       * @param request 

115.       * @param response 

116.       * @param remainingTime 

117.       *            剩余限制时间 

118.       * @throws ServletException 

119.       * @throws IOException 

120.       */  

121.      private void forward(HttpServletRequest request, HttpServletResponse response, long remainingTime)  

122.              throws ServletException, IOException {  

123.          request.setAttribute("remainingTime", remainingTime);  

124.          request.getRequestDispatcher("/error/overLimitIP").forward(request, response);  

125.      }  

126.    

127.      /** 

128.       * @Description 是否是被限制的IP 

129.       * @author zhangyd 

130.       * @date 201688 下午5:39:17 

131.       * @param limitedIpMap 

132.       * @param ip 

133.       * @return true : 被限制 | false : 正常 

134.       */  

135.      private boolean isLimitedIP(ConcurrentHashMap<String, Long> limitedIpMap, String ip) {  

136.          if (limitedIpMap == null || limitedIpMap.isEmpty() || ip == null) {  

137.              // 没有被限制  

138.              return false;  

139.          }  

140.          return limitedIpMap.containsKey(ip);  

141.      }  

142.    

143.      /** 

144.       * 初始化用户访问次数和访问时间 

145.       *  

146.       * @author zhangyd 

147.       * @date 2016729 上午10:01:39 

148.       * @param ipMap 

149.       * @param ip 

150.       */  

151.      private void initIpVisitsNumber(ConcurrentHashMap<String, Long[]> ipMap, String ip) {  

152.          Long[] ipInfo = new Long[2];  

153.          ipInfo[0] = 0L;// 访问次数  

154.          ipInfo[1] = System.currentTimeMillis();// 初次访问时间  

155.          ipMap.put(ip, ipInfo);  

156.      }  

157.    

158.  }  

 

 为了方便测试,我把封禁时间调到1分钟

 

 

Java代码  

1. /** 

2.  * 默认限制时间(单位:ms 

3.  */  

4. private static final long LIMITED_TIME_MILLIS = 60 * 1000;  

5.   

6. /** 

7.  * 用户连续访问最高阀值,超过该值则认定为恶意操作的IP,进行限制 

8.  */  

9. private static final int LIMIT_NUMBER = 20;  

10.  

11./** 

12. * 用户访问最小安全时间,在该时间内如果访问次数大于阀值,则记录为恶意IP,否则视为正常访问 

13. */  

14.private static final int MIN_SAFE_TIME = 5000;  

 上面这三项是自定义的,根据自己情况来。

 

 加入Spring Task定时器。配置文件:applicationContext.xml

 

Java代码  

1.  <context:component-scan base-package="com.test" >  

2.      <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />  

3.  </context:component-scan>  

4.  <!-- 配置定时任务 -->  

5.  <task:annotation-driven />  

 IPFilterTask

Java代码  

1.  import java.util.Date;  

2.  import java.util.Iterator;  

3.  import java.util.Map.Entry;  

4.  import java.util.concurrent.ConcurrentHashMap;  

5.    

6.  import javax.servlet.ServletContext;  

7.    

8.  import org.springframework.scheduling.annotation.Scheduled;  

9.  import org.springframework.stereotype.Component;  

10. import org.springframework.web.context.ContextLoaderListener;  

11.   

12. /** 

13.  * @Description IP限制过滤器定时任务,过滤受限的IP,剔除已经到期的限制IP 

14.  * @author zhangyd 

15.  * @date 2016818 上午9:39:19 

16.  * @version V1.0 

17.  * @since JDK  1.7 

18.  * @modify IP限制过滤手动触发改为定时任务 

19.  */  

20. @Component  

21. public class IPFilterTask {  

22.   

23.     /** 

24.      * @Description 30s执行一次过滤操作 

25.      * @author zhangyd 

26.      * @date 2016817 下午5:49:55 

27.      */  

28.     @Scheduled(cron = "0/30 * *  * * ? ")  

29.     public void filterLimitedIpMap() {  

30.         ServletContext context = ContextLoaderListener.getCurrentWebApplicationContext().getServletContext();  

31.         @SuppressWarnings("unchecked")  

32.         ConcurrentHashMap<String, Long> limitedIpMap = (ConcurrentHashMap<String, Long>) context  

33.                 .getAttribute("limitedIpMap");  

34.         if (limitedIpMap.isEmpty()) {  

35.             return;  

36.         }  

37.         Iterator<Entry<String, Long>> it = limitedIpMap.entrySet().iterator();  

38.         long currentTimeMillis = System.currentTimeMillis();  

39.         while (it.hasNext()) {  

40.             Entry<String, Long> e = it.next();  

41.             long expireTimeMillis = e.getValue();  

42.             if (expireTimeMillis <= currentTimeMillis) {  

43.                 it.remove();  

44.                 System.out.println(new Date() + "时,去掉了一个限制用户[" + e.getKey() + "]");  

45.             }  

46.         }  

47.     }  

48. }  

 

 

测试:

演示统共分三步:

第一步:正常访问并且间隔时间略长,访问20次为第一步

第二步:按住F5狂刷,一直到跳转到限制页面为第二步

第三步:等待1min,限制时间过后,重新刷新页面


 

方法四、session控制

public void doGet(HttpServletRequestrequest, HttpServletResponse response)
   throws ServletException, IOException {

  response.setContentType("text/html;charset=utf-8");
  PrintWriter out = response.getWriter();
  out
    .println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTDHTML 4.01 Transitional//EN\">");
  out.println("<HTML>");
  out.println("  <HEAD><TITLE>AServlet</TITLE></HEAD>");
  out.println("  <BODY>");
  HttpSession session = request.getSession(true);
  Object count = session.getAttribute("COUNTER");
  int counter = 0;
  if (count == null) {
   counter = 1;
   // 将第一次计数存入session
   session.setAttribute("COUNTER", new Integer(1));
  } else {
   counter = ((Integer) count).intValue();
   counter++;// 计数加一
   // 将计数存入session
   session.setAttribute("COUNTER", newInteger(counter));
  }

  // 输出信息
  out.println(" 欢迎你" + counter + "次访问xx网站!");

  out.println(" </BODY>");
  out.println("</HTML>");
  out.flush();
  out.close();
 }

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值