利用自定义注解,解决问题
场景:前端发起的频繁的请求,导致服务器压力过大。需要对后端接口进行限流处理,每个接口都需要做限流处理的话就会导致代码冗余,此时就可以利用注解进行解决
这里是用于 某个用户点击的次数(结合radis)进行判断,也可以用请求的ip进行判断
核心代码:
- //限流,60秒5次
- ValueOperations valueOperations = redisTemplate.opsForValue();
- String uri = request.getRequestURI();
- //captcha = "0"; 这个用于测试
- Integer count =(Integer) valueOperations.get(uri+":"+user.getId());
- if(count == null){
- valueOperations.set(uri+":"+user.getId(),1,60,TimeUnit.SECONDS);
- } else if (count<5) {
- valueOperations.increment(uri+":"+user.getId());
- }else {
- return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REAHCED);
- }
完整代码:
- @RequestMapping(value = "/path",method = RequestMethod.GET)
- @ResponseBody
- public RespBean getPath(User user,Long goodsId,String captcha,HttpServletRequest request){
- if(user == null){
- return RespBean.error(RespBeanEnum.SESSION_ERROR);
- }
- //限流,60秒5次
- ValueOperations valueOperations = redisTemplate.opsForValue();
- String uri = request.getRequestURI();
- log.info("uri====:{}",uri);
- log.info("qequestIp===={}",request.getRemoteAddr());
- log.info("LocalIp===={}",request.getLocalAddr());
- //captcha = "0";
- Integer count =(Integer) valueOperations.get(uri+":"+user.getId());
- if(count == null){
- valueOperations.set(uri+":"+user.getId(),1,60,TimeUnit.SECONDS);
- } else if (count<5) {
- valueOperations.increment(uri+":"+user.getId());
- }else {
- return RespBean.error(RespBeanEnum.ACCESS_LIMIT_REAHCED);
- }
-
- boolean check= orderService.checkCaptcha(user,goodsId,captcha);
- if(!check){
- return RespBean.error(RespBeanEnum.ERROR_CAPTCHA);
- }
-
- String str = orderService.createPath(user,goodsId);
- return RespBean.success(str);
- }
注解定义方式,以AcessLimit为例
定义注解 AccessLimit
创建拦截器 AccessLimitInterceptor
在WebConfig 中添加拦截器 addInterceptors
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface AccessLimit {
-
- //时间
- int second();
- //次数
- int maxCount();
-
- //这个用于判断是否需要登录,后续可用SpringSecurity + JWT 实现。
- boolean needLogin() default true;
- }
核心代码:
- @Component
- public class AccessLimitInterceptor implements HandlerInterceptor {
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- if(handler instanceof HandlerMethod){
- HandlerMethod hm = (HandlerMethod) handler;
- AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
- if( accessLimit ==null){
- return true;
- }
- int second = accessLimit.second();
- int maxCount = accessLimit.maxCount();
- boolean needLogin = accessLimit.needLogin();
- String key = request.getRequestURI();
-
- ValueOperations valueOperations =redisTemplate.opsForValue();
- Integer count = (Integer) valueOperations.get(key);
- if(count == null){
- valueOperations.set(key,1,second, TimeUnit.SECONDS);
- } else if (count
- valueOperations.increment(key);
- } else {
- render(response,RespBeanEnum.ACCESS_LIMIT_REAHCED);
- return false;
- }
- }
-
- return true;
- }
完整代码:
- package com.xxx.seckilldemo.annotation;
-
-
-
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.xxx.seckilldemo.config.UserContext;
- import com.xxx.seckilldemo.pojo.User;
- import com.xxx.seckilldemo.service.IUserService;
- import com.xxx.seckilldemo.utils.CookieUtil;
- import com.xxx.seckilldemo.vo.RespBean;
- import com.xxx.seckilldemo.vo.RespBeanEnum;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Component;
- import org.springframework.util.StringUtils;
- import org.springframework.web.method.HandlerMethod;
- import org.springframework.web.servlet.HandlerInterceptor;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.io.ObjectInput;
- import java.io.PrintWriter;
- import java.util.concurrent.TimeUnit;
-
- /** 接口限流拦截器
- * @author Adzc
- * @date 2023/11/15 15:40
- */
- @Component
- public class AccessLimitInterceptor implements HandlerInterceptor {
-
-
- @Autowired
- private IUserService userService;
-
- @Autowired
- private RedisTemplate redisTemplate;
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- if(handler instanceof HandlerMethod){
- User user = getUser(request,response);
- UserContext.setUser(user);
- HandlerMethod hm = (HandlerMethod) handler;
- AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
- if( accessLimit ==null){
- return true;
- }
- int second = accessLimit.second();
- int maxCount = accessLimit.maxCount();
-
- //此处是登录相关
- boolean needLogin = accessLimit.needLogin();
- String key = request.getRequestURI();
- if(needLogin){
- if (user == null){
- render(response, RespBeanEnum.SESSION_ERROR);
- return false;
- }
- key+=":"+user.getId();
- }
-
- //在有效时间内,次数的判断
- ValueOperations valueOperations =redisTemplate.opsForValue();
- Integer count = (Integer) valueOperations.get(key);
- if(count == null){
- valueOperations.set(key,1,second, TimeUnit.SECONDS);
- } else if (count
- valueOperations.increment(key);
- } else {
- //返回对象的定义
- render(response,RespBeanEnum.ACCESS_LIMIT_REAHCED);
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * 获取当前用户
- * @param request
- * @param response
- * @return
- */
- private User getUser(HttpServletRequest request,HttpServletResponse response){
- String ticket = CookieUtil.getCookieValue(request,"userTicket");
- if (StringUtils.isEmpty(ticket)){
- System.out.println("没有获取到用户");
- return null;
- }
- return userService.getUserByCookie(ticket,request,response);
- }
-
- /**
- * 构建返回对象
- * @param response
- * @param respBeanEnum
- */
- private void render(HttpServletResponse response,RespBeanEnum respBeanEnum) throws IOException {
- response.setContentType("application/json");
- response.setCharacterEncoding("utf-8");
- PrintWriter out = response.getWriter();
- RespBean respBean = RespBean.error(respBeanEnum);
- out.write(new ObjectMapper().writeValueAsString(respBean));
- out.flush();
- out.close();
-
- }
- }
返回对象用到了枚举
- /**
- * 公共返回对象
- * @author Adzc
- * @date 2023/9/25 23:19
- */
-
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- @ToString
- public class RespBean {
-
- private long code;
- private String message;
- private Object obj;
-
- /**
- * 成功返回结果
- * @return
- */
- public static RespBean success(){
- return new RespBean(RespBeanEnum.SUCCESS.getCode(), RespBeanEnum.SUCCESS.getMessage(),null);
- }
-
- public static RespBean success(Object obj){
- return new RespBean(RespBeanEnum.SUCCESS.getCode(), RespBeanEnum.SUCCESS.getMessage(),obj);
- }
-
- /**
- * 失败返回结果
- * @param respBeanEnum
- * @return
- */
- public static RespBean error(RespBeanEnum respBeanEnum){
- return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(),null);
- }
-
- public static RespBean error(RespBeanEnum respBeanEnum,Object obj){
- return new RespBean(respBeanEnum.getCode(), respBeanEnum.getMessage(),obj);
- }
- }
- @Getter
- @ToString
- @AllArgsConstructor
- public enum RespBeanEnum {
-
- SUCCESS(200,"SUCCESS"),
- ERROR(500,"服务端异常"),
-
- //登录模块
- LOGIN_ERROR(500210,"用户名或密码不正确"),
- MOBILE_ERROR(500211,"非法手机号"),
- BIND_ERROR(500212,"参数校验异常"),
- MOBILE_NOT_EXIST(500213, "手机号不存在"),
- PASSWORD_UPDATE_FAIL(500214,"密码更新失败"),
- SESSION_ERROR(500215,"用户不存在"),
- ACCESS_LIMIT_REAHCED(500504,"访问过于频繁请稍后在尝试");
- private final Integer code;
-
- private final String message;
- }
在WebConfig 中添加拦截器 addInterceptors
核心代码
-
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(accessLimitInterceptor);
- }
-
- }
完整代码
- @Configuration
- @EnableWebMvc
- public class WebConfig implements WebMvcConfigurer {
-
- @Autowired
- private AccessLimitInterceptor accessLimitInterceptor;
-
-
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(accessLimitInterceptor);
- }
- }
例2 利用@JsonSerialize注解+自定义类进行手机号脱敏处理
场景:很多手机号之类的信息需要在前端页面需要进行脱敏处理
- public class PhoneJsonSerializer extends JsonSerializer {
- @Override
- public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
- if (value != null && value instanceof String){
- if (!Objects.equals(value,"") && value.toString().length() > 5){
- String phone = value.toString();
- phone = phone.substring(0, 3) + "******" + phone.substring(phone.length() - 2);
- gen.writeString(phone);
- } else {
- gen.writeObject(value);
- }
- } else {
- gen.writeObject(value);
- }
- }
-
- }
在返回的对象中使用
- @JsonSerialize(using = PhoneJsonSerializer.class)
- private String phone;
-
相关阅读:
有什么可替代问卷星的好用的问卷工具?
2022年9月19日--9月25日(ue4热更新视频教程为主,)
PeLK:101 x 101 的超大卷积网络,同参数量下反超 ViT | CVPR 2024
通过 SingleFlight 模式学习 Go 并发编程
防火墙技术:一种保护网络安全的安全模型及其编程实现
【无标题】
L61.linux命令每日一练 -- 第九章 Linux进程管理命令 -- renice和nohup
基于NIFI工具搭建生产级别的IIOT系统
P1006 [NOIP2008 提高组] 传纸条,棋盘型dp,路径dp
使用 AJAX 提升网页数据的动态交互
-
原文地址:https://blog.csdn.net/weixin_53630942/article/details/134496894