增加限流的操作类型,补充CommonController注解

This commit is contained in:
D 2024-01-25 10:58:24 +08:00
parent ad1b71e32e
commit 6cf353c31e
5 changed files with 70 additions and 27 deletions

37
doc/限流逻辑.drawio Normal file
View File

@ -0,0 +1,37 @@
<mxfile host="65bd71144e">
<diagram id="PZYBk4sDCuIX38xMYcUk" name="第 1 页">
<mxGraphModel dx="900" dy="563" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="4" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="2" target="3">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="2" value="标记有RateLimiter注解的方法会被拦截" style="html=1;" vertex="1" parent="1">
<mxGeometry x="160" y="80" width="380" height="50" as="geometry"/>
</mxCell>
<mxCell id="6" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="3" target="5">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="3" value="获取注解上的时间和次数" style="html=1;" vertex="1" parent="1">
<mxGeometry x="160" y="150" width="380" height="50" as="geometry"/>
</mxCell>
<mxCell id="8" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="5" target="7">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="&amp;nbsp;通过拼接{KEY}-{IP/USERID/DEPTID}-{ClassName}-{MethodName}形成rediskey" style="html=1;" vertex="1" parent="1">
<mxGeometry x="100" y="230" width="500" height="50" as="geometry"/>
</mxCell>
<mxCell id="10" value="" style="edgeStyle=none;html=1;" edge="1" parent="1" source="7" target="9">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="7" value="通过redisTemplate.excute来检测time时间内最大请求次数" style="html=1;" vertex="1" parent="1">
<mxGeometry x="100" y="310" width="500" height="50" as="geometry"/>
</mxCell>
<mxCell id="9" value="通过返回的number再次确认请求次数" style="html=1;" vertex="1" parent="1">
<mxGeometry x="100" y="390" width="500" height="50" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -25,6 +25,7 @@ import com.ruoyi.framework.config.ServerConfig;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
@ -85,7 +86,7 @@ public class CommonController {
*/ */
@Operation(summary = "通用上传请求(单个)") @Operation(summary = "通用上传请求(单个)")
@PostMapping("/upload") @PostMapping("/upload")
public AjaxResult uploadFile(MultipartFile file) throws Exception { public AjaxResult uploadFile(@RequestBody MultipartFile file) throws Exception {
try { try {
// 上传文件路径 // 上传文件路径
String filePath = RuoYiConfig.getUploadPath(); String filePath = RuoYiConfig.getUploadPath();
@ -108,7 +109,7 @@ public class CommonController {
*/ */
@Operation(summary = "通用上传请求(多个)") @Operation(summary = "通用上传请求(多个)")
@PostMapping("/uploads") @PostMapping("/uploads")
public AjaxResult uploadFiles(List<MultipartFile> files) public AjaxResult uploadFiles(@RequestBody List<MultipartFile> files)
throws Exception { throws Exception {
try { try {
// 上传文件路径 // 上传文件路径

View File

@ -5,6 +5,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.enums.LimitType; import com.ruoyi.common.enums.LimitType;

View File

@ -6,8 +6,7 @@ package com.ruoyi.common.enums;
* @author ruoyi * @author ruoyi
*/ */
public enum LimitType public enum LimitType {
{
/** /**
* 默认策略全局限流 * 默认策略全局限流
*/ */
@ -16,5 +15,15 @@ public enum LimitType
/** /**
* 根据请求者IP进行限流 * 根据请求者IP进行限流
*/ */
IP IP,
/**
* 根据请求者的用户ID进行限流
*/
USER,
/**
* 根据请求者的部门进行限流
*/
DEPT,
} }

View File

@ -3,6 +3,7 @@ package com.ruoyi.framework.aspectj;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Before;
@ -13,9 +14,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript; import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.RateLimiter; import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.enums.LimitType; import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.ip.IpUtils;
@ -26,8 +29,7 @@ import com.ruoyi.common.utils.ip.IpUtils;
*/ */
@Aspect @Aspect
@Component @Component
public class RateLimiterAspect public class RateLimiterAspect {
{
private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
private RedisTemplate<Object, Object> redisTemplate; private RedisTemplate<Object, Object> redisTemplate;
@ -35,50 +37,43 @@ public class RateLimiterAspect
private RedisScript<Long> limitScript; private RedisScript<Long> limitScript;
@Autowired @Autowired
public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate) public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate) {
{
this.redisTemplate = redisTemplate; this.redisTemplate = redisTemplate;
} }
@Autowired @Autowired
public void setLimitScript(RedisScript<Long> limitScript) public void setLimitScript(RedisScript<Long> limitScript) {
{
this.limitScript = limitScript; this.limitScript = limitScript;
} }
@Before("@annotation(rateLimiter)") @Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
{
int time = rateLimiter.time(); int time = rateLimiter.time();
int count = rateLimiter.count(); int count = rateLimiter.count();
String combineKey = getCombineKey(rateLimiter, point); String combineKey = getCombineKey(rateLimiter, point);
List<Object> keys = Collections.singletonList(combineKey); List<Object> keys = Collections.singletonList(combineKey);
try try {
{
Long number = redisTemplate.execute(limitScript, keys, count, time); Long number = redisTemplate.execute(limitScript, keys, count, time);
if (StringUtils.isNull(number) || number.intValue() > count) if (StringUtils.isNull(number) || number.intValue() > count) {
{
throw new ServiceException("访问过于频繁,请稍候再试"); throw new ServiceException("访问过于频繁,请稍候再试");
} }
log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);
} } catch (ServiceException e) {
catch (ServiceException e)
{
throw e; throw e;
} } catch (Exception e) {
catch (Exception e)
{
throw new RuntimeException("服务器限流异常,请稍候再试"); throw new RuntimeException("服务器限流异常,请稍候再试");
} }
} }
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
{
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP) if (rateLimiter.limitType() == LimitType.IP) {
{
stringBuffer.append(IpUtils.getIpAddr()).append("-"); stringBuffer.append(IpUtils.getIpAddr()).append("-");
} else if (rateLimiter.limitType() == LimitType.USER) {
stringBuffer.append(SecurityUtils.getUserId()).append("-");
} else if (rateLimiter.limitType() == LimitType.DEPT) {
stringBuffer.append(SecurityUtils.getDeptId()).append("-");
} }
MethodSignature signature = (MethodSignature) point.getSignature(); MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod(); Method method = signature.getMethod();