v1.4.1
Parker 4 years ago
parent 62d212a9e9
commit 2ec4ef2d67

@ -0,0 +1,24 @@
package org.opsli.common.annotation.limiter;
import org.opsli.common.enums.AlertType;
import org.opsli.common.utils.RateLimiterUtil;
import java.lang.annotation.*;
/**
* Java
* @author Parker
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Limiter {
/** 标题 */
double qps() default RateLimiterUtil.DEFAULT_QPS;
/** 提醒方式 */
AlertType alertType() default AlertType.JSON;
}

@ -25,6 +25,9 @@ public interface OrderConstants {
/** Util 加载顺序 */
int UTIL_ORDER = 140;
/** 限流器 */
int LIMITER_AOP_SORT = 149;
/** token */
int TOKEN_AOP_SORT = 150;

@ -0,0 +1,17 @@
package org.opsli.common.enums;
/**
* @Author: Parker
* @CreateTime: 2020-09-17 23:40
* @Description:
*/
public enum AlertType {
/** alert 弹出 */
ALERT(),
/** JSON 回推*/
JSON,
;
}

@ -30,6 +30,8 @@ public enum CommonMsg implements BaseMsg {
/** Controller 参数默认序列化 */
EXCEPTION_CONTROLLER_MODEL(10100,"序列化对象失败!"),
/** 创建文件失败 */
EXCEPTION_CREATE_FILE_ERROR(10101,"创建文件失败!"),
;

@ -0,0 +1,62 @@
package org.opsli.common.utils;
import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
import org.opsli.common.exception.ServiceException;
import org.opsli.common.msg.CommonMsg;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
/**
* @BelongsProject: think-bboss-parent
* @BelongsPackage: com.think.bboss.common.utils
* @Author: Parker
* @CreateTime: 2021-01-05 14:26
* @Description:
*/
@Slf4j
public final class OutputStreamUtil {
/**
* WriterOutputStream
*/
public static OutputStream getOutputStream(String fileName, HttpServletResponse response)
throws ServiceException {
try {
fileName = new String(fileName.getBytes(), StandardCharsets.ISO_8859_1);
response.addHeader("Content-Disposition", "attachment; filename=" + fileName);
return response.getOutputStream();
} catch (IOException e) {
// 创建文件失败
throw new ServiceException(CommonMsg.EXCEPTION_CREATE_FILE_ERROR);
}
}
/**
*
*/
public static void exceptionResponse(String msg, HttpServletResponse response){
try {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8;");
PrintWriter writer = response.getWriter();
writer.write(
"<script type=\"text/javascript\">alert('"+msg+"');</script>");
writer.flush();
// 关闭流
IoUtil.close(writer);
}catch (Exception e){
log.error(e.getMessage(), e);
}
}
// ==========================
private OutputStreamUtil(){}
}

@ -0,0 +1,147 @@
package org.opsli.common.utils;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.concurrent.ConcurrentMap;
/**
* @BelongsProject: think-bboss-parent
* @BelongsPackage: com.think.bboss.common.utils
* @Author: Parker
* @CreateTime: 2021-01-05 16:06
* @Description:
*/
@Slf4j
public final class RateLimiterUtil {
/** 默认QPS */
public static final double DEFAULT_QPS = 10d;
/** 默认等待时长 */
private static final int DEFAULT_WAIT = 5000;
/** key-value (service,Qps) IP的限制速率 */
private static final ConcurrentMap<String,Double> IP_MAP;
/** userkey-service,limiter ,限制用户对接口的访问速率 */
private static final ConcurrentMap<String, RateLimiter> IP_LIMITER_MAP;
static{
IP_MAP = Maps.newConcurrentMap();
IP_LIMITER_MAP = Maps.newConcurrentMap();
}
/**
* IP QPS
* @param ip
* @param qps
*/
public static void updateIpQps(String ip, double qps) {
IP_MAP.put(ip,qps);
}
/**
* IP
* @param ip
*/
public static void removeIp(String ip) {
IP_MAP.remove(ip);
IP_LIMITER_MAP.remove(ip);
}
/**
*
* @param request
* @return
*/
public static boolean enter(HttpServletRequest request) {
// 获得IP
String clientIpAddress = IPUtil.getClientIpAddress(request);
return RateLimiterUtil.enter(clientIpAddress);
}
/**
*
* @param request
* @return
*/
public static boolean enter(HttpServletRequest request, Double dfQps) {
// 获得IP
String clientIpAddress = IPUtil.getClientIpAddress(request);
return RateLimiterUtil.enter(clientIpAddress, dfQps);
}
/**
*
* @param clientIpAddress IP
* @return
*/
public static boolean enter(String clientIpAddress) {
return RateLimiterUtil.enter(clientIpAddress, null);
}
/**
*
* @param clientIpAddress IP
* @param dfQps QPS
* @return
*/
public static boolean enter(String clientIpAddress, Double dfQps) {
// 计时器
TimeInterval timer = DateUtil.timer();
Double qps = IP_MAP.get(clientIpAddress);
// 如果当前MAP IP为空 且指派IP不为空 则自动赋值
if(qps == null && dfQps != null){
RateLimiterUtil.updateIpQps(clientIpAddress, dfQps);
qps = dfQps;
}
//不限流
if (qps == null || qps == 0.0) {
return true;
}
RateLimiter rateLimiter = IP_LIMITER_MAP.get(clientIpAddress);
// 如果为空 则创建一个新的限流器
if (rateLimiter == null) {
rateLimiter = RateLimiter.create(qps);
RateLimiter putByOtherThread = IP_LIMITER_MAP.putIfAbsent(clientIpAddress, rateLimiter);
if (putByOtherThread != null) {
rateLimiter = putByOtherThread;
}
rateLimiter.setRate(qps);
}
//非阻塞
if (!rateLimiter.tryAcquire(Duration.ofMillis(DEFAULT_WAIT))) {
// 花费毫秒数
long timerCount = timer.interval();
//限速中,提示用户
log.info("限流器 耗时: "+ timerCount + "ms, 访问过频繁: " + clientIpAddress);
return false;
} else {
// 花费毫秒数
long timerCount = timer.interval();
//正常访问
log.info("限流器 耗时: "+ timerCount + "ms");
return true;
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
boolean enter = RateLimiterUtil.enter("127.0.0.1", RateLimiterUtil.DEFAULT_QPS);
System.out.println(enter);
}).start();
}
}
}

@ -0,0 +1,101 @@
/**
* Copyright 2020 OPSLI https://www.opsli.com
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.opsli.core.aspect;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.opsli.common.annotation.limiter.Limiter;
import org.opsli.common.enums.AlertType;
import org.opsli.common.exception.ServiceException;
import org.opsli.common.utils.OutputStreamUtil;
import org.opsli.common.utils.RateLimiterUtil;
import org.opsli.core.msg.CoreMsg;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import static org.opsli.common.constants.OrderConstants.LIMITER_AOP_SORT;
/**
*
*
* @author
* @date 2020-09-16
*/
@Slf4j
@Order(LIMITER_AOP_SORT)
@Aspect
@Component
public class LimiterAop {
@Pointcut("@annotation(org.opsli.common.annotation.limiter.Limiter)")
public void requestMapping() {
}
/**
*
* @param point
*/
@Before("requestMapping()")
public void limiterHandle(JoinPoint point){
try {
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
if(sra != null) {
HttpServletRequest request = sra.getRequest();
HttpServletResponse response = sra.getResponse();
Limiter limiter = method.getAnnotation(Limiter.class);
if(limiter != null){
AlertType alertType = limiter.alertType();
double qps = limiter.qps();
// 限流
boolean enterFlag = RateLimiterUtil.enter(request, qps);
if(!enterFlag){
// alert 弹出
if(AlertType.ALERT == alertType){
OutputStreamUtil.exceptionResponse(
CoreMsg.OTHER_EXCEPTION_LIMITER.getMessage(),
response
);
}else {
// 异常返回
throw new ServiceException(CoreMsg.OTHER_EXCEPTION_LIMITER);
}
}
}
}
}catch (ServiceException e){
throw e;
}catch (Exception e){
log.error(e.getMessage(),e);
}
}
}

@ -35,6 +35,7 @@ import org.opsli.common.annotation.hotdata.EnableHotData;
import org.opsli.common.exception.ServiceException;
import org.opsli.common.exception.TokenException;
import org.opsli.common.msg.CommonMsg;
import org.opsli.common.utils.OutputStreamUtil;
import org.opsli.common.utils.WrapperUtil;
import org.opsli.core.base.entity.BaseEntity;
import org.opsli.core.base.service.interfaces.CrudServiceInterface;
@ -292,7 +293,7 @@ public abstract class BaseRestController <T extends BaseEntity, E extends ApiWra
}
}catch (TokenException e){
// 推送错误信息
JwtRealm.exceptionResponse(e.getMessage(), response);
OutputStreamUtil.exceptionResponse(e.getMessage(), response);
return;
}
@ -342,7 +343,7 @@ public abstract class BaseRestController <T extends BaseEntity, E extends ApiWra
// 导出异常
if(!resultVo.isSuccess()){
// 无权访问该方法
JwtRealm.exceptionResponse(resultVo.getMsg(), response);
OutputStreamUtil.exceptionResponse(resultVo.getMsg(), response);
}
}

@ -66,6 +66,9 @@ public enum CoreMsg implements BaseMsg {
/** 演示模式 */
EXCEPTION_ENABLE_DEMO(10600,"演示模式不允许操作"),
/** 其他 */
OTHER_EXCEPTION_LIMITER(10700,"当前系统繁忙,请稍后再试"),
;
private final int code;

@ -155,22 +155,4 @@ public class JwtRealm extends AuthorizingRealm implements FlagRealm {
}
}
/**
*
*/
public static void exceptionResponse(String msg, HttpServletResponse response){
try {
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8;");
PrintWriter writer = response.getWriter();
writer.write(
"<script type=\"text/javascript\">alert('"+msg+"');</script>");
writer.flush();
// 关闭流
IoUtil.close(writer);
}catch (Exception e){
log.error(e.getMessage(), e);
}
}
}

@ -17,12 +17,8 @@ package org.opsli.core.creater.strategy.create;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import com.jfinal.kit.Kv;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.opsli.api.ApiFlag;
import org.opsli.common.enums.DictType;
import org.opsli.common.enums.ValiArgsType;
import org.opsli.common.utils.HumpUtil;
import org.opsli.common.utils.Props;
@ -30,7 +26,6 @@ import org.opsli.common.utils.ZipUtils;
import org.opsli.core.creater.strategy.create.backend.JavaCodeBuilder;
import org.opsli.core.creater.strategy.create.foreend.VueCodeBuilder;
import org.opsli.core.creater.strategy.create.readme.ReadMeBuilder;
import org.opsli.core.creater.utils.EnjoyUtil;
import org.opsli.modulars.creater.column.wrapper.CreaterTableColumnModel;
import org.opsli.modulars.creater.createrlogs.wrapper.CreaterBuilderModel;
import org.opsli.modulars.creater.table.wrapper.CreaterTableAndColumnModel;
@ -40,7 +35,6 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@ -15,7 +15,6 @@
*/
package org.opsli.core.creater.strategy.create.foreend;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import com.jfinal.kit.Kv;
import org.apache.commons.lang3.StringUtils;

Loading…
Cancel
Save