caojianbin
8 months ago
22 changed files with 1260 additions and 30 deletions
@ -0,0 +1,17 @@ |
|||||||
|
package com.ecell.internationalize.auth.utils; |
||||||
|
|
||||||
|
import com.ecell.internationalize.common.core.utils.SpringUtils; |
||||||
|
import com.ecell.internationalize.common.security.service.TokenService; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
public class AuthLogic { |
||||||
|
public TokenService tokenServiceImpl = SpringUtils.getBean(TokenService.class); |
||||||
|
/** |
||||||
|
* 会话注销,根据指定Token |
||||||
|
*/ |
||||||
|
public void logoutByToken(String token) { |
||||||
|
tokenServiceImpl.delLoginUser(token); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.ecell.internationalize.auth.utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
public class AuthUtil { |
||||||
|
/** |
||||||
|
* 底层的 AuthLogic 对象 |
||||||
|
*/ |
||||||
|
public static AuthLogic authLogic = new AuthLogic(); |
||||||
|
/** |
||||||
|
* 会话注销,根据指定Token |
||||||
|
* |
||||||
|
* @param token 指定token |
||||||
|
*/ |
||||||
|
public static void logoutByToken(String token) { |
||||||
|
authLogic.logoutByToken(token); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.ecell.internationalize.gateway; |
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication; |
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; |
||||||
|
|
||||||
|
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class }) |
||||||
|
public class EcellGatewayApplication { |
||||||
|
public static void main(String[] args) |
||||||
|
{ |
||||||
|
SpringApplication.run(EcellGatewayApplication.class, args); |
||||||
|
System.out.println("(♥◠‿◠)ノ゙ 易赛网关启动成功 ლ(´ڡ`ლ)゙"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
package com.ecell.internationalize.gateway.config; |
||||||
|
|
||||||
|
import com.google.code.kaptcha.Constants; |
||||||
|
import com.google.code.kaptcha.impl.DefaultKaptcha; |
||||||
|
import com.google.code.kaptcha.util.Config; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class CaptchaConfig { |
||||||
|
|
||||||
|
@Bean(name = "captchaProducer") |
||||||
|
public DefaultKaptcha getKaptchaBean() |
||||||
|
{ |
||||||
|
DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); |
||||||
|
Properties properties = new Properties(); |
||||||
|
// 是否有边框 默认为true 我们可以自己设置yes,no
|
||||||
|
properties.setProperty(Constants.KAPTCHA_BORDER, "yes"); |
||||||
|
// 验证码文本字符颜色 默认为Color.BLACK
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); |
||||||
|
// 验证码图片宽度 默认为200
|
||||||
|
properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "160"); |
||||||
|
// 验证码图片高度 默认为50
|
||||||
|
properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "60"); |
||||||
|
// 验证码文本字符大小 默认为40
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); |
||||||
|
// KAPTCHA_SESSION_KEY
|
||||||
|
properties.setProperty(Constants.KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); |
||||||
|
// 验证码文本字符长度 默认为5
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); |
||||||
|
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); |
||||||
|
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
|
||||||
|
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); |
||||||
|
Config config = new Config(properties); |
||||||
|
defaultKaptcha.setConfig(config); |
||||||
|
return defaultKaptcha; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean(name = "captchaProducerMath") |
||||||
|
public DefaultKaptcha getKaptchaBeanMath() |
||||||
|
{ |
||||||
|
DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); |
||||||
|
Properties properties = new Properties(); |
||||||
|
// 是否有边框 默认为true 我们可以自己设置yes,no
|
||||||
|
properties.setProperty(Constants.KAPTCHA_BORDER, "yes"); |
||||||
|
// 边框颜色 默认为Color.BLACK
|
||||||
|
properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "105,179,90"); |
||||||
|
// 验证码文本字符颜色 默认为Color.BLACK
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); |
||||||
|
// 验证码图片宽度 默认为200
|
||||||
|
properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "160"); |
||||||
|
// 验证码图片高度 默认为50
|
||||||
|
properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "60"); |
||||||
|
// 验证码文本字符大小 默认为40
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); |
||||||
|
// KAPTCHA_SESSION_KEY
|
||||||
|
properties.setProperty(Constants.KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); |
||||||
|
// 验证码文本生成器
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.gateway.config.KaptchaTextCreator"); |
||||||
|
// 验证码文本字符间距 默认为2
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); |
||||||
|
// 验证码文本字符长度 默认为5
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); |
||||||
|
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
||||||
|
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); |
||||||
|
// 验证码噪点颜色 默认为Color.BLACK
|
||||||
|
properties.setProperty(Constants.KAPTCHA_NOISE_COLOR, "white"); |
||||||
|
// 干扰实现类
|
||||||
|
properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); |
||||||
|
// 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
|
||||||
|
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); |
||||||
|
Config config = new Config(properties); |
||||||
|
defaultKaptcha.setConfig(config); |
||||||
|
return defaultKaptcha; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,72 @@ |
|||||||
|
package com.ecell.internationalize.gateway.config; |
||||||
|
|
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants; |
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; |
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem; |
||||||
|
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; |
||||||
|
import com.alibaba.csp.sentinel.slots.block.RuleConstant; |
||||||
|
import com.ecell.internationalize.gateway.handler.SentinelFallbackHandler; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.core.Ordered; |
||||||
|
import org.springframework.core.annotation.Order; |
||||||
|
import org.springframework.core.io.ClassPathResource; |
||||||
|
import org.springframework.data.redis.core.script.DefaultRedisScript; |
||||||
|
import org.springframework.scripting.support.ResourceScriptSource; |
||||||
|
|
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class GatewayConfig { |
||||||
|
@Bean |
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE) |
||||||
|
public SentinelFallbackHandler sentinelGatewayExceptionHandler() |
||||||
|
{ |
||||||
|
return new SentinelFallbackHandler(); |
||||||
|
} |
||||||
|
/** |
||||||
|
* |
||||||
|
* 初始化限流规则 |
||||||
|
* */ |
||||||
|
// @PostConstruct
|
||||||
|
public void doInit(){ |
||||||
|
initGatewayRules(); |
||||||
|
|
||||||
|
} |
||||||
|
/** |
||||||
|
* 硬编码网关限流规则 |
||||||
|
*/ |
||||||
|
private void initGatewayRules() { |
||||||
|
Set<GatewayFlowRule> rules = new HashSet<>(); |
||||||
|
|
||||||
|
GatewayFlowRule rule = new GatewayFlowRule(); |
||||||
|
// 指定限流模式, 根据 route_id 做限流, 默认的模式
|
||||||
|
rule.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID); |
||||||
|
// 指定 route_id -> service id
|
||||||
|
rule.setResource("yisai-system"); |
||||||
|
GatewayParamFlowItem paramItem = new GatewayParamFlowItem(); |
||||||
|
paramItem.setFieldName("appId"); |
||||||
|
rule.setParamItem(paramItem); |
||||||
|
// 按照 QPS 限流
|
||||||
|
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); |
||||||
|
// 统计窗口和限流阈值
|
||||||
|
rule.setIntervalSec(60); |
||||||
|
rule.setCount(3); |
||||||
|
|
||||||
|
rules.add(rule); |
||||||
|
|
||||||
|
// 加载到网关中
|
||||||
|
GatewayRuleManager.loadRules(rules); |
||||||
|
} |
||||||
|
@Bean |
||||||
|
public DefaultRedisScript<Long> limitScript(){ |
||||||
|
DefaultRedisScript<Long> script = new DefaultRedisScript<>(); |
||||||
|
script.setResultType(Long.class); |
||||||
|
script.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua\\rateLimit.lua"))); |
||||||
|
return script; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
package com.ecell.internationalize.gateway.config; |
||||||
|
|
||||||
|
import com.google.code.kaptcha.text.impl.DefaultTextCreator; |
||||||
|
|
||||||
|
import java.util.Random; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
public class KaptchaTextCreator extends DefaultTextCreator { |
||||||
|
private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getText() |
||||||
|
{ |
||||||
|
Integer result = 0; |
||||||
|
Random random = new Random(); |
||||||
|
int x = random.nextInt(10); |
||||||
|
int y = random.nextInt(10); |
||||||
|
StringBuilder suChinese = new StringBuilder(); |
||||||
|
int randomoperands = (int) Math.round(Math.random() * 2); |
||||||
|
if (randomoperands == 0) |
||||||
|
{ |
||||||
|
result = x * y; |
||||||
|
suChinese.append(CNUMBERS[x]); |
||||||
|
suChinese.append("*"); |
||||||
|
suChinese.append(CNUMBERS[y]); |
||||||
|
} |
||||||
|
else if (randomoperands == 1) |
||||||
|
{ |
||||||
|
if (!(x == 0) && y % x == 0) |
||||||
|
{ |
||||||
|
result = y / x; |
||||||
|
suChinese.append(CNUMBERS[y]); |
||||||
|
suChinese.append("/"); |
||||||
|
suChinese.append(CNUMBERS[x]); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
result = x + y; |
||||||
|
suChinese.append(CNUMBERS[x]); |
||||||
|
suChinese.append("+"); |
||||||
|
suChinese.append(CNUMBERS[y]); |
||||||
|
} |
||||||
|
} |
||||||
|
else if (randomoperands == 2) |
||||||
|
{ |
||||||
|
if (x >= y) |
||||||
|
{ |
||||||
|
result = x - y; |
||||||
|
suChinese.append(CNUMBERS[x]); |
||||||
|
suChinese.append("-"); |
||||||
|
suChinese.append(CNUMBERS[y]); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
result = y - x; |
||||||
|
suChinese.append(CNUMBERS[y]); |
||||||
|
suChinese.append("-"); |
||||||
|
suChinese.append(CNUMBERS[x]); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
result = x + y; |
||||||
|
suChinese.append(CNUMBERS[x]); |
||||||
|
suChinese.append("+"); |
||||||
|
suChinese.append(CNUMBERS[y]); |
||||||
|
} |
||||||
|
suChinese.append("=?@" + result); |
||||||
|
return suChinese.toString(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
package com.ecell.internationalize.gateway.config.properties; |
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
@RefreshScope |
||||||
|
@ConfigurationProperties(prefix = "security.captcha") |
||||||
|
public class CaptchaProperties { |
||||||
|
/** |
||||||
|
* 验证码开关 |
||||||
|
*/ |
||||||
|
private Boolean enabled; |
||||||
|
|
||||||
|
/** |
||||||
|
* 验证码类型(math 数组计算 char 字符) |
||||||
|
*/ |
||||||
|
private String type; |
||||||
|
|
||||||
|
public Boolean getEnabled() |
||||||
|
{ |
||||||
|
return enabled; |
||||||
|
} |
||||||
|
|
||||||
|
public void setEnabled(Boolean enabled) |
||||||
|
{ |
||||||
|
this.enabled = enabled; |
||||||
|
} |
||||||
|
|
||||||
|
public String getType() |
||||||
|
{ |
||||||
|
return type; |
||||||
|
} |
||||||
|
|
||||||
|
public void setType(String type) |
||||||
|
{ |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.ecell.internationalize.gateway.config.properties; |
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
@RefreshScope |
||||||
|
@ConfigurationProperties(prefix = "security.ignore") |
||||||
|
public class IgnoreWhiteProperties { |
||||||
|
/** |
||||||
|
* 放行白名单配置,网关不校验此处的白名单 |
||||||
|
*/ |
||||||
|
private List<String> whites = new ArrayList<>(); |
||||||
|
|
||||||
|
public List<String> getWhites() |
||||||
|
{ |
||||||
|
return whites; |
||||||
|
} |
||||||
|
|
||||||
|
public void setWhites(List<String> whites) |
||||||
|
{ |
||||||
|
this.whites = whites; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
package com.ecell.internationalize.gateway.constant; |
||||||
|
|
||||||
|
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||||
|
import org.springframework.web.server.ServerWebExchange; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
public class MessagesConstant { |
||||||
|
private static final org.slf4j.Logger log = LoggerFactory.getLogger(MessagesConstant.class); |
||||||
|
/**令牌不能为空*/ |
||||||
|
public static final String MESSAGES_TOKE_EMPTY_EN="Token cannot be empty"; |
||||||
|
public static final String MESSAGES_TOKE_EMPTY_ZH="令牌不能为空"; |
||||||
|
/**令牌已过期或验证不正确*/ |
||||||
|
public static final String MESSAGES_TOKE_EMPTY_OR_ERROR_EN="Token expired or incorrectly verified"; |
||||||
|
public static final String MESSAGES_TOKE_EMPTY_OR_ERROR_ZH="令牌已过期或验证不正确"; |
||||||
|
/**登录状态已过期*/ |
||||||
|
public static final String MESSAGES_LOGIN_STATUS_EXPIRED_EN="Login status has expired"; |
||||||
|
public static final String MESSAGES_LOGIN_STATUS_EXPIRED_ZH="登录状态已过期"; |
||||||
|
/**令牌验证失败*/ |
||||||
|
public static final String MESSAGES_TOKE_VERIFICATION_FAILED_EN="Token verification failed"; |
||||||
|
public static final String MESSAGES_TOKE_VERIFICATION_FAILED_ZH="令牌验证失败"; |
||||||
|
/**appId 不能为空*/ |
||||||
|
public static final String MESSAGES_APPID_ISEMPTY_FAILED_EN="appId cannot be empty"; |
||||||
|
public static final String MESSAGES_APPID_ISEMPTY_FAILED_ZH="appId 不能为空"; |
||||||
|
/**appKey 不能为空*/ |
||||||
|
public static final String MESSAGES_APPkEY_ISEMPTY_FAILED_EN="appKey cannot be empty"; |
||||||
|
public static final String MESSAGES_APPkEY_ISEMPTY_FAILED_ZH="appKey 不能为空"; |
||||||
|
/**请求地址不允许访问*/ |
||||||
|
public static final String MESSAGES_REQUEST_ADDRESS_EN="The requested address does not allow access"; |
||||||
|
public static final String MESSAGES_REQUEST_ADDRESS_ZH="请求地址不允许访问"; |
||||||
|
/**服务未找到*/ |
||||||
|
public static final String MESSAGES_SERVICE_NOT_FOUND_EN="Service not found"; |
||||||
|
public static final String MESSAGES_SERVICE_NOT_FOUND_ZH="服务未找到"; |
||||||
|
/**内部服务器错误*/ |
||||||
|
public static final String MESSAGES_INTERNAL_SERVER_ERROR_EN="Internal server error"; |
||||||
|
public static final String MESSAGES_INTERNAL_SERVER_ERROR_ZH="内部服务器错误"; |
||||||
|
|
||||||
|
/**内部服务器错误*/ |
||||||
|
public static final String MESSAGES_API_STATE_ERROR_EN="This account has been disabled"; |
||||||
|
public static final String MESSAGES_API_STATE_ERROR_ZH="该账户已被禁用"; |
||||||
|
|
||||||
|
public static final String MESSAGES_API_LIMIT_MAX_ERROR_ZH="当前接口达到最大限流次数"; |
||||||
|
public static final String MESSAGES_API_LIMIT_MAX_ERROR_EN="The current interface has reached the maximum current limit times"; |
||||||
|
|
||||||
|
|
||||||
|
public static final Integer APP_ID_CODE=406; |
||||||
|
public static final Integer APP_KEY_CODE=407; |
||||||
|
public static final Integer STATE_CODE=408; |
||||||
|
public static final Integer LIMIT_CODE=409; |
||||||
|
|
||||||
|
public static final String STRING_ZERO="0"; |
||||||
|
public static final String STRING_ONE="1"; |
||||||
|
|
||||||
|
|
||||||
|
/**语言类型 中文*/ |
||||||
|
public static final String Language_ZH="zh"; |
||||||
|
/**语言类型 英文*/ |
||||||
|
public static final String Language_EN="en"; |
||||||
|
/**语言类型 key*/ |
||||||
|
public static final String HEADER_KEY="yisailanguage"; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 国际化处理 |
||||||
|
* @param exchange |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static String getMessages(ServerWebExchange exchange, String zh_msg, String en_msg) { |
||||||
|
//给一个默认值
|
||||||
|
String msg = en_msg; |
||||||
|
ServerHttpRequest request = exchange.getRequest(); |
||||||
|
if (StringUtils.isNotNull(request)) { |
||||||
|
if (request.getHeaders().containsKey(MessagesConstant.HEADER_KEY)) { |
||||||
|
String zh = exchange.getRequest().getHeaders().getFirst(MessagesConstant.HEADER_KEY); |
||||||
|
if (MessagesConstant.Language_ZH.equals(zh)) { |
||||||
|
msg = zh_msg; |
||||||
|
} else { |
||||||
|
msg = en_msg; |
||||||
|
} |
||||||
|
} else { |
||||||
|
List<String> list = request.getHeaders().get("accept-language"); |
||||||
|
if (StringUtils.isNotEmpty(list) && list.size() > 0) { |
||||||
|
String s = list.get(0).split(",")[0].split("-")[0]; |
||||||
|
log.info("获取头部的accept-language值:", request.getHeaders().get("accept-language")); |
||||||
|
log.info("获取头部的语言:{}", s); |
||||||
|
if (MessagesConstant.Language_ZH.equals(s)) { |
||||||
|
msg = zh_msg; |
||||||
|
} else { |
||||||
|
msg = en_msg; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
} else { |
||||||
|
msg = en_msg; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return msg; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
package com.ecell.internationalize.gateway.constant; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
public class RateLimitInfo implements Serializable { |
||||||
|
private static final long serialVersionUID=1L; |
||||||
|
@ApiModelProperty(example = "APPID") |
||||||
|
private Integer appId; |
||||||
|
|
||||||
|
@ApiModelProperty(example = "APPKEY") |
||||||
|
private String appKey; |
||||||
|
|
||||||
|
@ApiModelProperty(example = "状态(0:禁用 , 1:启用)") |
||||||
|
private String status; |
||||||
|
|
||||||
|
@ApiModelProperty(example = "厂商Id") |
||||||
|
private String firmId; |
||||||
|
|
||||||
|
@ApiModelProperty(example = "标识(1:厂商:2:代理商)") |
||||||
|
private String firmFlag; |
||||||
|
|
||||||
|
@ApiModelProperty(example = "代理商Id") |
||||||
|
private String secondFirmId; |
||||||
|
|
||||||
|
@ApiModelProperty(example = "发起请求秒数(如一分钟多少次,单位为秒)") |
||||||
|
private Integer second; |
||||||
|
|
||||||
|
@ApiModelProperty(example = "发起请求次数(如多少分钟100次)") |
||||||
|
private Integer frequency; |
||||||
|
|
||||||
|
public Integer getAppId() { |
||||||
|
return appId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAppId(Integer appId) { |
||||||
|
this.appId = appId; |
||||||
|
} |
||||||
|
|
||||||
|
public String getAppKey() { |
||||||
|
return appKey; |
||||||
|
} |
||||||
|
|
||||||
|
public void setAppKey(String appKey) { |
||||||
|
this.appKey = appKey; |
||||||
|
} |
||||||
|
|
||||||
|
public String getStatus() { |
||||||
|
return status; |
||||||
|
} |
||||||
|
|
||||||
|
public void setStatus(String status) { |
||||||
|
this.status = status; |
||||||
|
} |
||||||
|
|
||||||
|
public String getFirmId() { |
||||||
|
return firmId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setFirmId(String firmId) { |
||||||
|
this.firmId = firmId; |
||||||
|
} |
||||||
|
|
||||||
|
public String getFirmFlag() { |
||||||
|
return firmFlag; |
||||||
|
} |
||||||
|
|
||||||
|
public void setFirmFlag(String firmFlag) { |
||||||
|
this.firmFlag = firmFlag; |
||||||
|
} |
||||||
|
|
||||||
|
public String getSecondFirmId() { |
||||||
|
return secondFirmId; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSecondFirmId(String secondFirmId) { |
||||||
|
this.secondFirmId = secondFirmId; |
||||||
|
} |
||||||
|
|
||||||
|
public Integer getSecond() { |
||||||
|
return second; |
||||||
|
} |
||||||
|
|
||||||
|
public void setSecond(Integer second) { |
||||||
|
this.second = second; |
||||||
|
} |
||||||
|
|
||||||
|
public Integer getFrequency() { |
||||||
|
return frequency; |
||||||
|
} |
||||||
|
|
||||||
|
public void setFrequency(Integer frequency) { |
||||||
|
this.frequency = frequency; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,211 @@ |
|||||||
|
package com.ecell.internationalize.gateway.filter; |
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON; |
||||||
|
import com.alibaba.fastjson2.JSONObject; |
||||||
|
import com.ecell.internationalize.common.core.constant.CacheConstants; |
||||||
|
import com.ecell.internationalize.common.core.constant.HttpStatus; |
||||||
|
import com.ecell.internationalize.common.core.constant.SecurityConstants; |
||||||
|
import com.ecell.internationalize.common.core.constant.TokenConstants; |
||||||
|
import com.ecell.internationalize.common.core.utils.JwtUtils; |
||||||
|
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||||
|
import com.ecell.internationalize.common.core.utils.StringUtils; |
||||||
|
import com.ecell.internationalize.common.redis.service.RedisService; |
||||||
|
import com.ecell.internationalize.gateway.config.properties.IgnoreWhiteProperties; |
||||||
|
import com.ecell.internationalize.gateway.constant.MessagesConstant; |
||||||
|
import com.ecell.internationalize.gateway.constant.RateLimitInfo; |
||||||
|
import io.jsonwebtoken.Claims; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain; |
||||||
|
import org.springframework.cloud.gateway.filter.GlobalFilter; |
||||||
|
import org.springframework.core.Ordered; |
||||||
|
import org.springframework.data.redis.core.script.RedisScript; |
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.springframework.web.server.ServerWebExchange; |
||||||
|
import reactor.core.publisher.Mono; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
@Component |
||||||
|
public class AuthFilter implements GlobalFilter, Ordered { |
||||||
|
private static final Logger log = LoggerFactory.getLogger(AuthFilter.class); |
||||||
|
private static final String API_URL = "/yisai-system/api/**"; |
||||||
|
private static final String RATE_LIMIT = "rate_limit:"; |
||||||
|
private static final String HEADER_APPID = "appid"; |
||||||
|
private static final String HEADER_APPKEY = "appkey"; |
||||||
|
|
||||||
|
// 排除过滤的 uri 地址,nacos自行添加
|
||||||
|
@Autowired |
||||||
|
private IgnoreWhiteProperties ignoreWhite; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
private RedisService redisService; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
RedisScript<Long> redisScript; |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { |
||||||
|
ServerHttpRequest request = exchange.getRequest(); |
||||||
|
ServerHttpRequest.Builder mutate = request.mutate(); |
||||||
|
|
||||||
|
String url = request.getURI().getPath(); |
||||||
|
if (StringUtils.isMatch(API_URL, url)) { |
||||||
|
return getVoidMono(exchange, chain, request, url); |
||||||
|
// return chain.filter(exchange);
|
||||||
|
} |
||||||
|
// 跳过不需要验证的路径
|
||||||
|
if (StringUtils.matches(url, ignoreWhite.getWhites())) { |
||||||
|
return chain.filter(exchange); |
||||||
|
} |
||||||
|
String token = getToken(request); |
||||||
|
if (StringUtils.isEmpty(token)) { |
||||||
|
|
||||||
|
return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_TOKE_EMPTY_ZH, MessagesConstant.MESSAGES_TOKE_EMPTY_EN)); |
||||||
|
} |
||||||
|
Claims claims = JwtUtils.parseToken(token); |
||||||
|
if (claims == null) { |
||||||
|
return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_TOKE_EMPTY_OR_ERROR_ZH, MessagesConstant.MESSAGES_TOKE_EMPTY_OR_ERROR_EN)); |
||||||
|
} |
||||||
|
String userkey = JwtUtils.getUserKey(claims); |
||||||
|
boolean islogin = redisService.hasKey(getTokenKey(userkey)); |
||||||
|
if (!islogin) { |
||||||
|
return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_LOGIN_STATUS_EXPIRED_ZH, MessagesConstant.MESSAGES_LOGIN_STATUS_EXPIRED_EN)); |
||||||
|
} |
||||||
|
String userid = JwtUtils.getUserId(claims); |
||||||
|
String username = JwtUtils.getUserName(claims); |
||||||
|
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)) { |
||||||
|
return unauthorizedResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_TOKE_VERIFICATION_FAILED_ZH, MessagesConstant.MESSAGES_TOKE_VERIFICATION_FAILED_EN)); |
||||||
|
} |
||||||
|
|
||||||
|
// 设置用户信息到请求
|
||||||
|
addHeader(mutate, SecurityConstants.USER_KEY, userkey); |
||||||
|
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid); |
||||||
|
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username); |
||||||
|
// 内部请求来源参数清除
|
||||||
|
removeHeader(mutate, SecurityConstants.FROM_SOURCE); |
||||||
|
return chain.filter(exchange.mutate().request(mutate.build()).build()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value) { |
||||||
|
if (value == null) { |
||||||
|
return; |
||||||
|
} |
||||||
|
String valueStr = value.toString(); |
||||||
|
String valueEncode = ServletUtils.urlEncode(valueStr); |
||||||
|
mutate.header(name, valueEncode); |
||||||
|
} |
||||||
|
|
||||||
|
private void removeHeader(ServerHttpRequest.Builder mutate, String name) { |
||||||
|
mutate.headers(httpHeaders -> httpHeaders.remove(name)).build(); |
||||||
|
} |
||||||
|
|
||||||
|
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg) { |
||||||
|
log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); |
||||||
|
|
||||||
|
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED); |
||||||
|
} |
||||||
|
|
||||||
|
private Mono<Void> unauthorizedAPIResponse(ServerWebExchange exchange, String msg, int code) { |
||||||
|
log.error("[对外接口鉴权异常处理]请求路径:{}", exchange.getRequest().getPath()); |
||||||
|
|
||||||
|
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, code); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取缓存key |
||||||
|
*/ |
||||||
|
private String getTokenKey(String token) { |
||||||
|
return CacheConstants.LOGIN_TOKEN_KEY + token; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取请求token |
||||||
|
*/ |
||||||
|
private String getToken(ServerHttpRequest request) { |
||||||
|
String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION); |
||||||
|
// 如果前端设置了令牌前缀,则裁剪掉前缀
|
||||||
|
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) { |
||||||
|
token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY); |
||||||
|
} |
||||||
|
return token; |
||||||
|
} |
||||||
|
|
||||||
|
private Mono<Void> getVoidMono(ServerWebExchange exchange, GatewayFilterChain chain, ServerHttpRequest request, String url) { |
||||||
|
Long apiCount; |
||||||
|
String appId = request.getHeaders().getFirst(HEADER_APPID); |
||||||
|
String appKey = request.getHeaders().getFirst(HEADER_APPKEY); |
||||||
|
if (StringUtils.isEmpty(appId)) { |
||||||
|
return unauthorizedAPIResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_APPID_ISEMPTY_FAILED_ZH, MessagesConstant.MESSAGES_APPID_ISEMPTY_FAILED_EN), MessagesConstant.APP_ID_CODE); |
||||||
|
} |
||||||
|
if (StringUtils.isEmpty(appId)) { |
||||||
|
return unauthorizedAPIResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_APPkEY_ISEMPTY_FAILED_ZH, MessagesConstant.MESSAGES_APPkEY_ISEMPTY_FAILED_EN), MessagesConstant.APP_KEY_CODE); |
||||||
|
} |
||||||
|
String redisKey = appId + "_" + appKey; |
||||||
|
|
||||||
|
if (!redisService.hasKey(redisKey)) { |
||||||
|
return unauthorizedAPIResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_ZH, MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_EN), 500); |
||||||
|
} |
||||||
|
try { |
||||||
|
Object cacheObject = redisService.getCacheObject(redisKey); |
||||||
|
String s = JSON.toJSONString(cacheObject); |
||||||
|
RateLimitInfo rateLimitInfo = JSONObject.parseObject(s, RateLimitInfo.class); |
||||||
|
if (StringUtils.isNotNull(rateLimitInfo)) { |
||||||
|
String status = rateLimitInfo.getStatus(); |
||||||
|
if (StringUtils.isNotEmpty(status)) { |
||||||
|
if (MessagesConstant.STRING_ZERO.equals(status)) { |
||||||
|
return unauthorizedAPIResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_API_STATE_ERROR_ZH, MessagesConstant.MESSAGES_API_STATE_ERROR_EN), MessagesConstant.STATE_CODE); |
||||||
|
} |
||||||
|
|
||||||
|
apiCount = getApiCount(rateLimitInfo, url); |
||||||
|
if (apiCount > rateLimitInfo.getFrequency()) { |
||||||
|
return unauthorizedAPIResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_API_LIMIT_MAX_ERROR_ZH, MessagesConstant.MESSAGES_API_LIMIT_MAX_ERROR_EN), MessagesConstant.LIMIT_CODE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
return unauthorizedAPIResponse(exchange, MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_ZH, MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_EN), 500); |
||||||
|
} |
||||||
|
|
||||||
|
return chain.filter(exchange); |
||||||
|
} |
||||||
|
|
||||||
|
private Long getApiCount(RateLimitInfo info, String url) { |
||||||
|
Long current; |
||||||
|
int time = 0, count = 0; |
||||||
|
String fid; |
||||||
|
StringBuffer buffer = new StringBuffer(RATE_LIMIT); |
||||||
|
if (MessagesConstant.STRING_ONE.equals(info.getFirmFlag())) { |
||||||
|
fid = info.getFirmId(); |
||||||
|
} else { |
||||||
|
fid = info.getSecondFirmId(); |
||||||
|
} |
||||||
|
String rateKey = buffer.append(fid).append(url).toString(); |
||||||
|
time = info.getSecond(); |
||||||
|
if (time <= 0) { |
||||||
|
time = 60; |
||||||
|
} |
||||||
|
count = info.getFrequency(); |
||||||
|
if (count <= 0) { |
||||||
|
count = 10; |
||||||
|
} |
||||||
|
|
||||||
|
current = (Long) redisService.redisTemplate.execute(redisScript, Collections.singletonList(rateKey), time, count); |
||||||
|
|
||||||
|
return current; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getOrder() { |
||||||
|
return -200; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
package com.ecell.internationalize.gateway.filter; |
||||||
|
|
||||||
|
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||||
|
import com.ecell.internationalize.gateway.constant.MessagesConstant; |
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilter; |
||||||
|
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
@Component |
||||||
|
public class BlackListUrlFilter extends AbstractGatewayFilterFactory<BlackListUrlFilter.Config> { |
||||||
|
@Override |
||||||
|
public GatewayFilter apply(Config config) { |
||||||
|
return (exchange, chain) -> { |
||||||
|
|
||||||
|
String url = exchange.getRequest().getURI().getPath(); |
||||||
|
if (config.matchBlacklist(url)) { |
||||||
|
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), MessagesConstant.getMessages(exchange, MessagesConstant.MESSAGES_REQUEST_ADDRESS_ZH, MessagesConstant.MESSAGES_REQUEST_ADDRESS_EN)); |
||||||
|
} |
||||||
|
|
||||||
|
return chain.filter(exchange); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public BlackListUrlFilter() { |
||||||
|
super(Config.class); |
||||||
|
} |
||||||
|
|
||||||
|
public static class Config { |
||||||
|
private List<String> blacklistUrl; |
||||||
|
|
||||||
|
private List<Pattern> blacklistUrlPattern = new ArrayList<>(); |
||||||
|
|
||||||
|
public boolean matchBlacklist(String url) { |
||||||
|
return !blacklistUrlPattern.isEmpty() && blacklistUrlPattern.stream().anyMatch(p -> p.matcher(url).find()); |
||||||
|
} |
||||||
|
|
||||||
|
public List<String> getBlacklistUrl() { |
||||||
|
return blacklistUrl; |
||||||
|
} |
||||||
|
|
||||||
|
public void setBlacklistUrl(List<String> blacklistUrl) { |
||||||
|
this.blacklistUrl = blacklistUrl; |
||||||
|
this.blacklistUrlPattern.clear(); |
||||||
|
this.blacklistUrl.forEach(url -> { |
||||||
|
this.blacklistUrlPattern.add(Pattern.compile(url.replaceAll("\\*\\*", "(.*?)"), Pattern.CASE_INSENSITIVE)); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
package com.ecell.internationalize.gateway.filter; |
||||||
|
|
||||||
|
import com.alibaba.nacos.shaded.org.checkerframework.checker.units.qual.C; |
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilter; |
||||||
|
import org.springframework.cloud.gateway.filter.GatewayFilterChain; |
||||||
|
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter; |
||||||
|
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; |
||||||
|
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; |
||||||
|
import org.springframework.http.HttpMethod; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.springframework.web.server.ServerWebExchange; |
||||||
|
import reactor.core.publisher.Mono; |
||||||
|
|
||||||
|
import java.util.Collections; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
@Component |
||||||
|
public class CacheRequestFilter extends AbstractGatewayFilterFactory<CacheRequestFilter.Config> { |
||||||
|
public CacheRequestFilter() |
||||||
|
{ |
||||||
|
super(Config.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String name() |
||||||
|
{ |
||||||
|
return "CacheRequestFilter"; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public GatewayFilter apply(Config config) |
||||||
|
{ |
||||||
|
CacheRequestGatewayFilter cacheRequestGatewayFilter = new CacheRequestGatewayFilter(); |
||||||
|
Integer order = config.getOrder(); |
||||||
|
if (order == null) |
||||||
|
{ |
||||||
|
return cacheRequestGatewayFilter; |
||||||
|
} |
||||||
|
return new OrderedGatewayFilter(cacheRequestGatewayFilter, order); |
||||||
|
} |
||||||
|
|
||||||
|
public static class CacheRequestGatewayFilter implements GatewayFilter |
||||||
|
{ |
||||||
|
@Override |
||||||
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) |
||||||
|
{ |
||||||
|
// GET DELETE 不过滤
|
||||||
|
HttpMethod method = exchange.getRequest().getMethod(); |
||||||
|
if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE) |
||||||
|
{ |
||||||
|
return chain.filter(exchange); |
||||||
|
} |
||||||
|
return ServerWebExchangeUtils.cacheRequestBodyAndRequest(exchange, (serverHttpRequest) -> { |
||||||
|
if (serverHttpRequest == exchange.getRequest()) |
||||||
|
{ |
||||||
|
return chain.filter(exchange); |
||||||
|
} |
||||||
|
return chain.filter(exchange.mutate().request(serverHttpRequest).build()); |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<String> shortcutFieldOrder() |
||||||
|
{ |
||||||
|
return Collections.singletonList("order"); |
||||||
|
} |
||||||
|
|
||||||
|
static class Config |
||||||
|
{ |
||||||
|
private Integer order; |
||||||
|
|
||||||
|
public Integer getOrder() |
||||||
|
{ |
||||||
|
return order; |
||||||
|
} |
||||||
|
|
||||||
|
public void setOrder(Integer order) |
||||||
|
{ |
||||||
|
this.order = order; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package com.ecell.internationalize.gateway.handler; |
||||||
|
|
||||||
|
import com.ecell.internationalize.common.core.utils.ServletUtils; |
||||||
|
import com.ecell.internationalize.gateway.constant.MessagesConstant; |
||||||
|
import org.slf4j.Logger; |
||||||
|
import org.slf4j.LoggerFactory; |
||||||
|
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; |
||||||
|
import org.springframework.cloud.gateway.support.NotFoundException; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.core.annotation.Order; |
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponse; |
||||||
|
import org.springframework.web.server.ResponseStatusException; |
||||||
|
import org.springframework.web.server.ServerWebExchange; |
||||||
|
import reactor.core.publisher.Mono; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author borui |
||||||
|
*/ |
||||||
|
@Order(-1) |
||||||
|
@Configuration |
||||||
|
public class GatewayExceptionHandler implements ErrorWebExceptionHandler { |
||||||
|
private static final Logger log = LoggerFactory.getLogger(GatewayExceptionHandler.class); |
||||||
|
|
||||||
|
@Override |
||||||
|
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) |
||||||
|
{ |
||||||
|
ServerHttpResponse response = exchange.getResponse(); |
||||||
|
|
||||||
|
if (exchange.getResponse().isCommitted()) |
||||||
|
{ |
||||||
|
return Mono.error(ex); |
||||||
|
} |
||||||
|
|
||||||
|
String msg; |
||||||
|
|
||||||
|
if (ex instanceof NotFoundException) |
||||||
|
{ |
||||||
|
/**服务未找到*/ |
||||||
|
msg = MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_SERVICE_NOT_FOUND_ZH,MessagesConstant.MESSAGES_SERVICE_NOT_FOUND_EN); |
||||||
|
|
||||||
|
} |
||||||
|
else if (ex instanceof ResponseStatusException) |
||||||
|
{ |
||||||
|
ResponseStatusException responseStatusException = (ResponseStatusException) ex; |
||||||
|
msg = responseStatusException.getMessage(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/**内部服务器错误*/ |
||||||
|
//msg="内部服务器错误";
|
||||||
|
msg =MessagesConstant.getMessages(exchange,MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_ZH,MessagesConstant.MESSAGES_INTERNAL_SERVER_ERROR_EN); |
||||||
|
} |
||||||
|
|
||||||
|
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage()); |
||||||
|
|
||||||
|
return ServletUtils.webFluxResponseWriter(response, msg); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
spring: |
||||||
|
redis: |
||||||
|
host: 120.77.209.176 |
||||||
|
port: 6379 |
||||||
|
database: 0 |
||||||
|
password: Ecell...20201001 |
||||||
|
cloud: |
||||||
|
gateway: |
||||||
|
discovery: |
||||||
|
locator: |
||||||
|
lowerCaseServiceId: true |
||||||
|
enabled: true |
||||||
|
routes: |
||||||
|
# 认证中心 |
||||||
|
- id: ecell-internationalize-auth |
||||||
|
uri: lb://ecell-internationalize-auth |
||||||
|
predicates: |
||||||
|
- Path=/auth/** |
||||||
|
filters: |
||||||
|
# 验证码处理 |
||||||
|
- CacheRequestFilter |
||||||
|
- ValidateCodeFilter |
||||||
|
- StripPrefix=1 |
||||||
|
#易赛系统服务 |
||||||
|
- id: yisai-system |
||||||
|
uri: lb://yisai-system |
||||||
|
predicates: |
||||||
|
- Path=/yisai_system/** |
||||||
|
filters: |
||||||
|
- StripPrefix=1 |
||||||
|
|
||||||
|
#易赛授权服务 |
||||||
|
- id: yisai-system-security |
||||||
|
uri: lb://yisai-system-security |
||||||
|
predicates: |
||||||
|
- Path=/yisai_security/** |
||||||
|
filters: |
||||||
|
- StripPrefix=1 |
||||||
|
#易赛APP服务 |
||||||
|
- id: yisai-app |
||||||
|
uri: lb://yisai-app |
||||||
|
predicates: |
||||||
|
- Path=/yisai_app/** |
||||||
|
filters: |
||||||
|
- StripPrefix=1 |
||||||
|
|
||||||
|
|
||||||
|
# 安全配置 |
||||||
|
security: |
||||||
|
# 验证码 |
||||||
|
captcha: |
||||||
|
enabled: true |
||||||
|
type: math |
||||||
|
# 防止XSS攻击 |
||||||
|
xss: |
||||||
|
enabled: true |
||||||
|
excludeUrls: |
||||||
|
- /system/notice |
||||||
|
# 不校验白名单 |
||||||
|
ignore: |
||||||
|
whites: |
||||||
|
- /auth/logout |
||||||
|
- /auth/login |
||||||
|
- /auth/register |
||||||
|
- /*/v2/api-docs |
||||||
|
- /csrf |
||||||
|
- /yisai_auth/*/login |
||||||
|
- /yisai_auth/*/logout |
||||||
|
- /yisai_api/api/** |
||||||
|
- /yisai_system/api/** |
||||||
|
cors: |
||||||
|
enabled: true |
||||||
|
|
||||||
|
|
@ -0,0 +1,40 @@ |
|||||||
|
# Tomcat |
||||||
|
server: |
||||||
|
port: 9997 |
||||||
|
|
||||||
|
# Spring |
||||||
|
spring: |
||||||
|
application: |
||||||
|
# 应用名称 |
||||||
|
name: ecell-internationalize-gateway |
||||||
|
profiles: |
||||||
|
# 环境配置 |
||||||
|
active: dev |
||||||
|
cloud: |
||||||
|
nacos: |
||||||
|
discovery: |
||||||
|
# 服务注册地址 |
||||||
|
server-addr: 127.0.0.1:8848 |
||||||
|
config: |
||||||
|
# 配置中心地址 |
||||||
|
server-addr: 127.0.0.1:8848 |
||||||
|
# 配置文件格式 |
||||||
|
file-extension: yml |
||||||
|
# 共享配置 |
||||||
|
shared-configs: |
||||||
|
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} |
||||||
|
# sentinel: |
||||||
|
# # 取消控制台懒加载 |
||||||
|
# eager: true |
||||||
|
# transport: |
||||||
|
# # 控制台地址 |
||||||
|
# dashboard: 127.0.0.1:8718 |
||||||
|
# # nacos配置持久化 |
||||||
|
# datasource: |
||||||
|
# ds1: |
||||||
|
# nacos: |
||||||
|
# server-addr: 127.0.0.1:8848 |
||||||
|
# dataId: sentinel-ruoyi-gateway |
||||||
|
# groupId: DEFAULT_GROUP |
||||||
|
# data-type: json |
||||||
|
# rule-type: flow |
@ -0,0 +1,15 @@ |
|||||||
|
local key = KEYS[1] |
||||||
|
local time = tonumber(ARGV[1]) |
||||||
|
local count = tonumber(ARGV[2]) |
||||||
|
local current = redis.call('get',key) |
||||||
|
if current and tonumber(current) > count then |
||||||
|
return tonumber(current) |
||||||
|
end |
||||||
|
current = redis.call('incr',key) |
||||||
|
if tonumber(current)==1 then |
||||||
|
redis.call('expire',key,time) |
||||||
|
end |
||||||
|
return tonumber(current) |
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in new issue