当前位置: 首页 > news >正文

邵阳网站制作建设软件编程基础知识

邵阳网站制作建设,软件编程基础知识,保定网站制作软件,wordpress防止篡改文件linux前言 场景: 为了限制短信验证码接口的访问次数#xff0c;防止被刷#xff0c;结合Aop和redis根据用户ip对用户限流 1.准备工作 首先我们创建一个 Spring Boot 工程#xff0c;引入 Web 和 Redis 依赖#xff0c;同时考虑到接口限流一般是通过注解来标记#xff0c;而注解…前言 场景: 为了限制短信验证码接口的访问次数防止被刷结合Aop和redis根据用户ip对用户限流 1.准备工作 首先我们创建一个 Spring Boot 工程引入 Web 和 Redis 依赖同时考虑到接口限流一般是通过注解来标记而注解是通过 AOP 来解析的所以我们还需要加上 AOP 的依赖最终的依赖如下 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependency然后提前准备好一个 Redis 实例这里我们项目配置好之后直接配置一下 Redis 的基本信息即可比如 spring.redis.hostlocalhost spring.redis.port6379 spring.redis.password1232.限流注解 接下来我们创建一个限流注解我们将限流分为两种情况 1针对当前接口的全局性限流例如该接口可以在 1 分钟内访问 100 次。 2 针对某一个 IP 地址的限流例如某个 IP 地址可以在 1 分钟内访问 100 次 针对这两种情况我们创建一个枚举类 public enum LimitType {/*** 默认策略全局限流*/DEFAULT,/*** 根据请求者IP进行限流*/IP } 接下来我们来创建限流注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface RateLimiter {/*** 限流key*/String key() default rate_limit:;/*** 限流时间,单位秒*/int time() default 60;/*** 限流次数*/int count() default 100;/*** 限流类型*/LimitType limitType() default LimitType.DEFAULT; }第一个参数限流的 key这个仅仅是一个前缀将来完整的 key 是这个前缀再加上接口方法的完整路径共同组成限流 key这个 key 将被存入到 Redis 中。 另外三个参数好理解我就不多说了。 好了将来哪个接口需要限流就在哪个接口上添加 RateLimiter 注解然后配置相关参数即可。 3. 定制或者选择redisTemplate 1. 定制 RedisTemplate(看需要,我使用第二种方案) 在 Spring Boot 中我们其实更习惯使用 Spring Data Redis 来操作 Redis不过默认的 RedisTemplate 有一个小坑就是序列化用的是 JdkSerializationRedisSerializer不知道小伙伴们有没有注意过直接用这个序列化工具将来存到 Redis 上的 key 和 value 都会莫名其妙多一些前缀这就导致你用命令读取的时候可能会出错。 例如存储的时候key 是 namevalue 是 javaboy但是当你在命令行操作的时候get name 却获取不到你想要的数据原因就是存到 redis 之后 name 前面多了一些字符此时只能继续使用 RedisTemplate 将之读取出来。 我们用 Redis 做限流会用到 Lua 脚本使用 Lua 脚本的时候就会出现上面说的这种情况所以我们需要修改 RedisTemplate 的序列化方案。 修改 RedisTemplate 序列化方案此配置用到了jackson2JsonRedisSerializer 的序列化器(忘了要不要引入依赖)代码参考案例如下 Configuration public class RedisConfig {Beanpublic RedisTemplateObject, Object redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplateObject, Object redisTemplate new RedisTemplate();redisTemplate.setConnectionFactory(connectionFactory);// 使用Jackson2JsonRedisSerialize 替换默认序列化(默认采用的是JDK序列化)Jackson2JsonRedisSerializerObject jackson2JsonRedisSerializer new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);return redisTemplate;} } 2.直接使用StringRedisTemplate StringRedisTemplate是Spring Data Redis定义好的一个操作redis的模版继承了redisTemplate默认使用了字符串序列序列化器就是key和value都是用String进行存储,定制RedisTemplate狭义无非是定义哪种存储类型的序列化器上面第一种数一种Json形式的序列化器在本文章中在实现限流功能上没有区别。 我选择这种的理由懒不用配置但是注意传入的key和value都要先转为toString()转为字符串不然会报错 4. 开放lua脚本 lua脚本操作Redis的目的就是为了保证多个Redis操作的原子性如果其中一条Redis操作出错了可以抛出异常给springboot进行Redis回滚。如果不知道怎么使用lua脚本可以去B站黑马Redis关于lua的几节补补。 2.脚本流程的意思大概如下: 首先获取到传进来的 key 以及 限流的 count 和时间 time。通过 get 获取到这个 key 对应的值这个值就是当前时间窗内这个接口可以访问多少次。如果是第一次访问此时拿到的结果为 nil否则拿到的结果应该是一个数字所以接下来就判断如果拿到的结果是一个数字并且这个数字还大于 count那 就说明已经超过流量限制了那么直接返回查询的结果即可。如果拿到的结果为 nil说明是第一次访问此时就给当前 key 自增 1然后设置一个过期时间。最后把自增 1 后的值返回就可以了。 -- redis限流脚本 -- key参数 local key KEYS[1] -- 限流的次数 local limitCount tonumber(ARGV[1]) -- 限流的时间 local limitTime tonumber(ARGV[2]) -- 获取当前时间 local currentCount redis.call(get, key) -- if 获取key的当前数 limitCount 则返回最大值 if currentCount and tonumber(currentCount) limitCount thenreturn tonumber(currentCount) end -- key 自增1 currentCount redis.call(incr,key) -- if key的值 1 设置过期限流过期时间 if tonumber(currentCount) 1 thenredis.call(expire,key,limitTime) end -- 返回key的值 return tonumber(currentCount) 5.注解解析 springboot记得在main方法开启aop注解功能(不会自己查)核心代码如下(看不懂问gpt)代码中的异常为自定义异常可换成自己的异常类抛出或处理异常 Component Aspect Slf4j public class RateLimiterAspect {Resourceprivate StringRedisTemplate stringRedisTemplate;Resourceprivate RedisScriptLong limitScript;Before(annotation(rateLimiter))public void doBefore(JoinPoint point, RateLimiter rateLimiter) {String key rateLimiter.key();int time rateLimiter.time();int count rateLimiter.count();String combineKey getCombineKey(rateLimiter, point);ListString keys Collections.singletonList(combineKey);try {Long number stringRedisTemplate.execute(limitScript, keys, String.valueOf(count), String.valueOf(time));if (numbernull || number.intValue() count) {throw new BusinessException(ErrorCode.PARAMS_ERROR,访问过于频繁请稍候再试);}log.info(限制请求{},当前请求{},缓存key{}, count, number.intValue(), keys.get(0));} catch (ServiceException e) {throw e;} catch (Exception e) {throw new BusinessException(ErrorCode.SYSTEM_ERROR, 系统繁忙请稍候再试);}}/*** 获取ip为key* param rateLimiter* param point* return*/public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {StringBuffer stringBuffer new StringBuffer(rateLimiter.key());if (rateLimiter.limitType() LimitType.IP) {stringBuffer.append(IpUtils.getIpAddr(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest())).append(-);}MethodSignature signature (MethodSignature) point.getSignature();Method method signature.getMethod();Class? targetClass method.getDeclaringClass();stringBuffer.append(targetClass.getName()).append(-).append(method.getName());return stringBuffer.toString();}} 根据HttpRequest获取用户ip的工具类(看不懂细节问gpt) public class IpUtils {public static String getIpAddr(HttpServletRequest request) {String ipAddress null;try {ipAddress request.getHeader(x-forwarded-for);if (ipAddress null || ipAddress.length() 0 || unknown.equalsIgnoreCase(ipAddress)) {ipAddress request.getHeader(Proxy-Client-IP);}if (ipAddress null || ipAddress.length() 0 || unknown.equalsIgnoreCase(ipAddress)) {ipAddress request.getHeader(WL-Proxy-Client-IP);}if (ipAddress null || ipAddress.length() 0 || unknown.equalsIgnoreCase(ipAddress)) {ipAddress request.getRemoteAddr();if (ipAddress.equals(127.0.0.1)) {// 根据网卡取本机配置的IPtry {ipAddress InetAddress.getLocalHost().getHostAddress();} catch (UnknownHostException e) {e.printStackTrace();}}}// 通过多个代理的情况第一个IP为客户端真实IP,多个IP按照,分割if (ipAddress ! null) {if (ipAddress.contains(,)) {return ipAddress.split(,)[0];} else {return ipAddress;}} else {return ;}} catch (Exception e) {e.printStackTrace();return ;}} } 6.接口测试 如下: 根据用户IP地址60秒只能调用一次接口 GetMapping(/message)RateLimiter(time 60,count 1,limitType LimitType.IP)public BaseResponseString sendMessage(String phone,HttpServletRequest request) {if (StringUtils.isBlank(phone)) {throw new BusinessException(ErrorCode.PARAMS_ERROR);}boolean result userVenueReservationService.sendMessage(phone,request);return ResultUtils.success(result ? 发送成功 : 发送失败);}有错再补~~
http://www.hkea.cn/news/14549607/

相关文章:

  • 手机网站有什么不同网站上微信支付功能
  • 网站建设要电脑网站你懂我意思正能量
  • 学校官方网站的建设目标是什么知彼网络网站建设
  • 网站域名怎么注册威海网站建设怎么样
  • 优化网站平台百度图片搜索引擎
  • 门户类网站有哪些网站的营销方法有哪些
  • 网站源码运行网页设计ppt模板
  • 网站名称注册程序html代码在线
  • 在阿里国际站做的网站营销活动策划公司
  • 台州网站推广技巧付费网站被挂了黑链
  • 单页静态网站怎么做wordpress 不发送邮件
  • wordpress怎么加站点图标门户网站建设案例
  • 网站平台建设费用的会计核算网站建设的电话销售好做不
  • 如何创建一个免费网站重庆是哪个省份
  • 新手学做网站难吗ui设计 国外网站
  • 北京网站制作招聘网肥城网站建设费用
  • 深圳网站开发哪个公司好如何在百度打广告
  • 茶叶外贸网站建设如果做网站赚钱
  • 外国网站怎么做扶风网站开发
  • 高要区住房和城乡建设局网站线上ui设计培训哪个好
  • 怎样用数据库做网站网站建设工作计划表
  • 小程序网站开发运行合同WordPress配置pdo
  • 城市生活网官方网站appiis做网站主目录选哪里
  • 网站设计与网页制作正文神华集团两学一做网站
  • 方案 网站有了主机如何做网站
  • 温州建站程序天津首页
  • 网站开发相关书籍资料网站 业务范围
  • 想自学广告设计怎么学如何对网站进行优化
  • 网站开发项目建设规范wordpress图文主题
  • 网站建设在国内外有何趋势书店网站开发目的和意义