利用自定义注解,AOP + redis限制ip访问接口次数
首先来一个注解
package co.yiiu.module.bountyHunter.pay.wxpay.core;import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;import java.lang.annotation.*;/*** Created date on 2018/12/10* Author Zy*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {/*** 允许访问的次数,默认值MAX_VALUE*/int count() default Integer.MAX_VALUE;/*** 时间段,单位为毫秒,默认值一分钟*/long time() default 60000;
}
利用AOP前置增强做逻辑处理
package co.yiiu.module.bountyHunter.pay.wxpay.core;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;@Slf4j
@Component
@Aspect
public class RequestLimitAop {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Before("within(@org.springframework.stereotype.Controller *) && @annotation(limit)")public void requestLimit(JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException {try {Object[] args = joinPoint.getArgs();HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();String ip = getIpAddress(request);String url = request.getRequestURL().toString();String key = "req_limit_".concat(url).concat("_").concat(ip);boolean checkResult = checkWithRedis(limit, key);if (!checkResult) {log.debug("requestLimited," + "[用户ip:{}],[访问地址:{}]超过了限定的次数[{}]次", ip, url, limit.count());throw new RequestLimitException("请求过于频繁,超出限制!");}} catch (RequestLimitException e) {throw e;} catch (Exception e) {log.error("RequestLimitAop.requestLimit has some exceptions: ", e);}}/*** 以redis实现请求记录** @param limit* @param key* @return*/private boolean checkWithRedis(RequestLimit limit, String key) {long count = stringRedisTemplate.opsForValue().increment(key, 1);if (count == 1) {stringRedisTemplate.expire(key, limit.time(), TimeUnit.MILLISECONDS);}if (count > limit.count()) {return false;}return true;}
/**
* 获取用户真实IP地址,不使用request.getRemoteAddr()的原因是有可能用户使用了代理软件方式避免真实IP地址,
* 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值
*
* @return ip
*/
private String getIpAddress(HttpServletRequest request) {String ip = request.getHeader("x-forwarded-for");log.info("x-forwarded-for ip: " + ip);if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {// 多次反向代理后会有多个ip值,第一个ip才是真实ipif( ip.indexOf(",")!=-1 ){ip = ip.split(",")[0];}}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");log.info("Proxy-Client-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");log.info("WL-Proxy-Client-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");log.info("HTTP_CLIENT_IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");log.info("HTTP_X_FORWARDED_FOR ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("X-Real-IP");log.info("X-Real-IP ip: " + ip);}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();log.info("getRemoteAddr ip: " + ip);}log.info("获取客户端ip: " + ip);return ip;}}
利用AOP后置捕获web请求的异常
**
package co.yiiu.core.exception;import co.yiiu.core.base.BaseController;
import co.yiiu.core.bean.Result;
import co.yiiu.module.user.pojo.User;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;@Slf4j
@Aspect
@Component
public class WebExceptionAspect extends BaseController{private Exception e;//切入点@Pointcut("execution(* co.yiiu.web.bountyHunter..*.*(..))")private void bountyHunterPointcut() {}/*** 拦截web层异常,记录异常日志,并返回友好信息到前端** @param e* 异常对象*/@AfterThrowing(pointcut = "bountyHunterPointcut()", throwing = "e")public void handleThrowing(JoinPoint joinPoint,Exception e) {User user = getUserFromSession();this.e = e;//e.printStackTrace();if (null != user){log.error("发现异常!操作用户手机号:"+user.getMobile());}log.error("发现异常!方法:"+ joinPoint.getSignature().getName()+"--->异常",e);//这里输入友好性信息if (!StringUtils.isEmpty(e.getMessage())){log.error("异常",e.getMessage());writeContent(500,e.getMessage());}else {writeContent(500,"十分抱歉,出现异常!程序猿小哥正在紧急抢修...");}}/*** 将内容输出到浏览器** @param content* 输出内容*/public static void writeContent(Integer code,String content) {HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();response.setCharacterEncoding("UTF-8");response.setHeader("Content-Type", "text/json;charset=UTF-8");response.setHeader("icop-content-type", "exception");PrintWriter writer = null;JsonGenerator jsonGenerator = null;try {writer = response.getWriter();jsonGenerator = (new ObjectMapper()).getFactory().createGenerator(writer);jsonGenerator.writeObject(Result.error(code,content));} catch (IOException e1) {e1.printStackTrace();}finally {writer.flush();writer.close();}}}
现在我们进行简单的测试
@PostMapping("/test")@RequestLimit(count = 2)@ResponseBodypublic Result test(){return Result.success();}
postman测试
控制台打印信息
---------------------
作者:IT界的奇葩
来源:CSDN
原文:https://blog.csdn.net/a309220728/article/details/84937630
版权声明:本文为博主原创文章,转载请附上博文链接!
利用自定义注解,AOP + redis限制ip访问接口次数相关推荐
- ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存
基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存 如何能更简洁的利用aop实现redis缓存,话不多说,上demo 需求: 数据查询时 ...
- SpringBoot自定义注解+AOP+redis实现防接口幂等性重复提交,从概念到实战
一.前言 在面试中,经常会有一道经典面试题,那就是:怎么防止接口重复提交? 小编也是背过的,好几种方式,但是一直没有实战过,做多了管理系统,发现这个事情真的没有过多的重视. 最近在测试过程中,发现了多 ...
- SpringBoot 自定义注解+AOP+Redis 防止接口重复提交表单数据
SpringBoot结合Redis处理重复提交 数据重复提交导致多次请求服务.入库,产生脏数据.冗余数据等情况.禁止重复提交使我们保证数据准确性及安全性的必要操作. 实际上,造成这种情况的场景不少: ...
- 如何利用自定义注解放行 Spring Security 项目的接口
在实际项目中使用到了springsecurity作为安全框架,我们会遇到需要放行一些接口,使其能匿名访问的业务需求.但是每当需要当需要放行时,都需要在security的配置类中进行修改,感觉非常的不优 ...
- Springboot整合多数据源(自定义注解+aop切面实现)
原理: 通过后台配置多个数据源,自定义注解,通过aop配置注解切面,前端调用需要传递数据源参数,根据判断数据源参数,调用相应的service或mapper方法. 实现: 准备俩个数据库:俩张表 表sq ...
- 实现权限控制_Spring自定义注解+AOP实现权限控制
本文转载于 SegmentFault 社区 作者:锦城 前言 作业系统在测试过程中出现了学生修改路由可以到达教师界面并且可以使用教师功能的问题,学生不用任何工具就可以修改自己的成绩,真的挺要命的,这就 ...
- redis 公网ip访问_Redis很重要,怎么只允许指定IP访问?
在 Linux 中安装了redis 服务,当在客户端通过远程连接的方式连接时,报could not connect错误. 错误的原因很简单,就是没有连接上redis服务,由于redis采用的安全策略, ...
- java 自定义注解+AOP实现日志记录
ssm版本: 1.首先自定义一个注解,该注解有两个属性,一个是模块名,一个是操作的内容.该注解是用来修饰Service层中的方法的. 2.创建一个切面类,该切面使用@Aspect和@Component ...
- 利用自定义注解实现权限验证
思路: 根据自定义注解,给对应权限能够查看到的资源的Controller方法上添加注解(也就是一个权限字符串),权限字符串跟方法是一对多的关系,同一个权限字符串可以添加到多个方法上:当用户有对应的权限 ...
- 自定义注解-aop实现日志记录
关于注解,平时接触的可不少,像是 @Controller.@Service.@Autowried 等等,不知道你是否有过这种疑惑,使用 @Service 注解的类成为我们的业务类,使用 @Contro ...
最新文章
- UVA816 Abbott的复仇 Abbott's Revenge(final的BFS)(真•答案)
- Linux上的Shell之FAQ
- java comparator排序顺序_Java 集合排序策略接口 Comparator
- 牛客网刷题(纯java题型 211~240题)
- BHIOT-833物联网智能网关
- linux定时任务_linux定时任务cron HelloWorld
- python怎么七个数字一换行_python中怎么换行?
- miniMobile(手机)
- 金融科技:科技生活化和生活金融化
- 普林斯顿微积分读本篇十五:积分方法
- STM32固件升级之 hypertrm(超级终端)使用(三)
- java程序的入口点_Java程序的入口点
- 仿真建模与仿真程序设计 Python
- Python数据结构与算法分析(第二版)答案 - 第二章(仅供参考)
- 计算机汉字怎么制作的,如何制作自己的字体?教你快速制作自己手写字体
- rust语言和cargo介绍
- linux 快照工具,技术预览:CentOS 7中利用Snapper GUI管理系统快照
- Java实习生常规技术面试题每日十题Java基础(四)
- 【知识图谱】实践篇——基于知识图谱的《红楼梦》人物关系可视化及问答系统实践:part7项目优化与打包
- Linux 命令行浏览器