first commit
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
package com.mhd.gateway;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
||||
/**
|
||||
* 网关启动程序
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class })
|
||||
public class MhdGatewayApplication
|
||||
{
|
||||
public static void main(String[] args)
|
||||
{
|
||||
SpringApplication.run(MhdGatewayApplication.class, args);
|
||||
System.out.println("(♥◠‿◠)ノ゙ 若依网关启动成功 ლ(´ڡ`ლ)゙ \n" +
|
||||
" .-------. ____ __ \n" +
|
||||
" | _ _ \\ \\ \\ / / \n" +
|
||||
" | ( ' ) | \\ _. / ' \n" +
|
||||
" |(_ o _) / _( )_ .' \n" +
|
||||
" | (_,_).' __ ___(_ o _)' \n" +
|
||||
" | |\\ \\ | || |(_,_)' \n" +
|
||||
" | | \\ `' /| `-' / \n" +
|
||||
" | | \\ / \\ / \n" +
|
||||
" ''-' `'-' `-..-' ");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.mhd.gateway.config;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import com.google.code.kaptcha.impl.DefaultKaptcha;
|
||||
import com.google.code.kaptcha.util.Config;
|
||||
import static com.google.code.kaptcha.Constants.*;
|
||||
|
||||
/**
|
||||
* 验证码配置
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Configuration
|
||||
public class CaptchaConfig
|
||||
{
|
||||
@Bean(name = "captchaProducer")
|
||||
public DefaultKaptcha getKaptchaBean()
|
||||
{
|
||||
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
|
||||
Properties properties = new Properties();
|
||||
// 是否有边框 默认为true 我们可以自己设置yes,no
|
||||
properties.setProperty(KAPTCHA_BORDER, "yes");
|
||||
// 验证码文本字符颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
|
||||
// 验证码图片宽度 默认为200
|
||||
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
|
||||
// 验证码图片高度 默认为50
|
||||
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
|
||||
// 验证码文本字符大小 默认为40
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
|
||||
// KAPTCHA_SESSION_KEY
|
||||
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
|
||||
// 验证码文本字符长度 默认为5
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
|
||||
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
||||
properties.setProperty(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(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(KAPTCHA_BORDER, "yes");
|
||||
// 边框颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
|
||||
// 验证码文本字符颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
|
||||
// 验证码图片宽度 默认为200
|
||||
properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
|
||||
// 验证码图片高度 默认为50
|
||||
properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
|
||||
// 验证码文本字符大小 默认为40
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
|
||||
// KAPTCHA_SESSION_KEY
|
||||
properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
|
||||
// 验证码文本生成器
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.mhd.gateway.config.KaptchaTextCreator");
|
||||
// 验证码文本字符间距 默认为2
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
|
||||
// 验证码文本字符长度 默认为5
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
|
||||
// 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
|
||||
properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
|
||||
// 验证码噪点颜色 默认为Color.BLACK
|
||||
properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
|
||||
// 干扰实现类
|
||||
properties.setProperty(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(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
|
||||
Config config = new Config(properties);
|
||||
defaultKaptcha.setConfig(config);
|
||||
return defaultKaptcha;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.mhd.gateway.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import com.mhd.gateway.handler.SentinelFallbackHandler;
|
||||
|
||||
/**
|
||||
* 网关限流配置
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Configuration
|
||||
public class GatewayConfig
|
||||
{
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SentinelFallbackHandler sentinelGatewayExceptionHandler()
|
||||
{
|
||||
return new SentinelFallbackHandler();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.mhd.gateway.config;
|
||||
|
||||
import java.util.Random;
|
||||
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
|
||||
|
||||
/**
|
||||
* 验证码文本生成器
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
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 = random.nextInt(3);
|
||||
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,31 @@
|
||||
package com.mhd.gateway.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.server.RequestPredicates;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import com.mhd.gateway.handler.ValidateCodeHandler;
|
||||
|
||||
/**
|
||||
* 路由配置信息
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Configuration
|
||||
public class RouterFunctionConfiguration
|
||||
{
|
||||
@Autowired
|
||||
private ValidateCodeHandler validateCodeHandler;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Bean
|
||||
public RouterFunction routerFunction()
|
||||
{
|
||||
return RouterFunctions.route(
|
||||
RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
|
||||
validateCodeHandler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.mhd.gateway.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.config.GatewayProperties;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.support.NameUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
|
||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
||||
import springfox.documentation.swagger.web.SwaggerResource;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
|
||||
/**
|
||||
* 聚合系统接口
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Component
|
||||
public class SwaggerProvider implements SwaggerResourcesProvider, WebFluxConfigurer
|
||||
{
|
||||
/**
|
||||
* Swagger2默认的url后缀
|
||||
*/
|
||||
public static final String SWAGGER2URL = "/v2/api-docs";
|
||||
|
||||
/**
|
||||
* 网关路由
|
||||
*/
|
||||
@Lazy
|
||||
@Autowired
|
||||
private RouteLocator routeLocator;
|
||||
|
||||
@Autowired
|
||||
private GatewayProperties gatewayProperties;
|
||||
|
||||
/**
|
||||
* 聚合其他服务接口
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<SwaggerResource> get()
|
||||
{
|
||||
List<SwaggerResource> resourceList = new ArrayList<>();
|
||||
List<String> routes = new ArrayList<>();
|
||||
// 获取网关中配置的route
|
||||
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
|
||||
gatewayProperties.getRoutes().stream()
|
||||
.filter(routeDefinition -> routes
|
||||
.contains(routeDefinition.getId()))
|
||||
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
|
||||
.filter(predicateDefinition -> "Path".equalsIgnoreCase(predicateDefinition.getName()))
|
||||
.filter(predicateDefinition -> !"mhd-auth".equalsIgnoreCase(routeDefinition.getId()))
|
||||
.forEach(predicateDefinition -> resourceList
|
||||
.add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs()
|
||||
.get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", SWAGGER2URL)))));
|
||||
return resourceList;
|
||||
}
|
||||
|
||||
private SwaggerResource swaggerResource(String name, String location)
|
||||
{
|
||||
SwaggerResource swaggerResource = new SwaggerResource();
|
||||
swaggerResource.setName(name);
|
||||
swaggerResource.setLocation(location);
|
||||
swaggerResource.setSwaggerVersion("2.0");
|
||||
return swaggerResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry)
|
||||
{
|
||||
/** swagger-ui 地址 */
|
||||
registry.addResourceHandler("/swagger-ui/**")
|
||||
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.mhd.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 mhd
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package com.mhd.gateway.config.properties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 放行白名单配置
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@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,44 @@
|
||||
package com.mhd.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 mhd
|
||||
*/
|
||||
@Configuration
|
||||
@RefreshScope
|
||||
@ConfigurationProperties(prefix = "security.world")
|
||||
public class WorldWhiteProperties
|
||||
{
|
||||
private String symbol;
|
||||
/**
|
||||
* 放行白名单配置,网关不校验此处的白名单
|
||||
*/
|
||||
private List<String> whites = new ArrayList<>();
|
||||
|
||||
public List<String> getWhites()
|
||||
{
|
||||
return whites;
|
||||
}
|
||||
|
||||
public void setWhites(List<String> whites)
|
||||
{
|
||||
this.whites = whites;
|
||||
}
|
||||
|
||||
public String getSymbol() {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
public void setSymbol(String symbol) {
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.mhd.gateway.config.properties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* XSS跨站脚本配置
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Configuration
|
||||
@RefreshScope
|
||||
@ConfigurationProperties(prefix = "security.xss")
|
||||
public class XssProperties
|
||||
{
|
||||
/**
|
||||
* Xss开关
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* 排除路径
|
||||
*/
|
||||
private List<String> excludeUrls = new ArrayList<>();
|
||||
|
||||
public Boolean getEnabled()
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public List<String> getExcludeUrls()
|
||||
{
|
||||
return excludeUrls;
|
||||
}
|
||||
|
||||
public void setExcludeUrls(List<String> excludeUrls)
|
||||
{
|
||||
this.excludeUrls = excludeUrls;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package com.mhd.gateway.filter;
|
||||
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.mhd.common.core.domain.po.UserConfigSwitchPo;
|
||||
import com.mhd.gateway.config.properties.IgnoreWhiteProperties;
|
||||
import com.mhd.gateway.config.properties.WorldWhiteProperties;
|
||||
import com.mhd.gateway.uilts.SensitiveWordLibrary;
|
||||
import com.mhd.gateway.uilts.SensitiveWordLoader;
|
||||
import com.mhd.gateway.uilts.TextSegmentation;
|
||||
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.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import com.mhd.common.core.constant.CacheConstants;
|
||||
import com.mhd.common.core.constant.HttpStatus;
|
||||
import com.mhd.common.core.constant.SecurityConstants;
|
||||
import com.mhd.common.core.constant.TokenConstants;
|
||||
import com.mhd.common.core.utils.JwtUtils;
|
||||
import com.mhd.common.core.utils.ServletUtils;
|
||||
import com.mhd.common.core.utils.StringUtils;
|
||||
import com.mhd.common.redis.service.RedisService;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 网关鉴权
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Component
|
||||
public class AuthFilter implements GlobalFilter, Ordered
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(AuthFilter.class);
|
||||
|
||||
// 排除过滤的 uri 地址,nacos自行添加
|
||||
@Autowired
|
||||
private IgnoreWhiteProperties ignoreWhite;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
//敏感词汇过滤 uri地址,nacos配置
|
||||
@Autowired
|
||||
private WorldWhiteProperties worldWhiteProperties;
|
||||
|
||||
|
||||
@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.matches(url, ignoreWhite.getWhites()))
|
||||
{
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
String token = getToken(request);
|
||||
if (StringUtils.isEmpty(token))
|
||||
{
|
||||
return unauthorizedResponse(exchange, "令牌不能为空");
|
||||
}
|
||||
Claims claims = JwtUtils.parseToken(token);
|
||||
if (claims == null)
|
||||
{
|
||||
return unauthorizedResponse(exchange, "令牌已过期或验证不正确!");
|
||||
}
|
||||
String userkey = JwtUtils.getUserKey(claims);
|
||||
boolean islogin = redisService.hasKey(getTokenKey(userkey));
|
||||
if (!islogin)
|
||||
{
|
||||
return unauthorizedResponse(exchange, "登录状态已过期");
|
||||
}
|
||||
String userid = JwtUtils.getUserId(claims);
|
||||
String username = JwtUtils.getUserName(claims);
|
||||
if (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username))
|
||||
{
|
||||
return unauthorizedResponse(exchange, "令牌验证失败");
|
||||
}
|
||||
//校验单点登录 0-否,1-是
|
||||
String topOrganizationId = JwtUtils.getTopOrganizationId(claims);
|
||||
UserConfigSwitchPo userConfigSwitchPo = redisService.getCacheObject(CacheConstants.USER_COMFIG_SWITCH_KEY + topOrganizationId);
|
||||
if (ObjUtil.isNotNull(userConfigSwitchPo)){
|
||||
Integer isSingleSign = userConfigSwitchPo.getIsSingleSign();
|
||||
if (ObjUtil.isNotNull(isSingleSign) && isSingleSign == 1) {
|
||||
String key = CacheConstants.USER_LOGIN_TOKENS + userid;
|
||||
String oldToken = CacheConstants.LOGIN_TOKEN_KEY + userkey;
|
||||
String currentToken = redisService.getCacheObject(key);
|
||||
if (StrUtil.isEmpty(currentToken)) {
|
||||
//重新登录 存储单点登录 用户-token
|
||||
redisService.deleteObject(oldToken);
|
||||
return unauthorizedResponse(exchange, "登录状态已过期,请重新登录");
|
||||
}
|
||||
//判断是否是单点登录保存的token,不一致则下线
|
||||
if (!StrUtil.equals(currentToken, oldToken)){
|
||||
redisService.deleteObject(oldToken);
|
||||
return unauthorizedResponse(exchange, "您已在其他设备登录,请重新登录");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
//是post请求的走获取body的方法
|
||||
String method = request.getMethodValue();
|
||||
if ("POST".equals(method)){
|
||||
// 校验路径,如果路径需要过滤,则进入脱敏方法
|
||||
if (StringUtils.matches(url, worldWhiteProperties.getWhites()))
|
||||
{
|
||||
return DataBufferUtils.join(exchange.getRequest().getBody())
|
||||
.flatMap(dataBuffer -> {
|
||||
byte[] bytes = new byte[dataBuffer.readableByteCount()];
|
||||
dataBuffer.read(bytes);
|
||||
String bodyString = new String(bytes, StandardCharsets.UTF_8);
|
||||
//进入脱敏方法
|
||||
if(StringUtils.isNotBlank(bodyString)){
|
||||
bodyString = worldFilter(bodyString);
|
||||
log.info("替换完成后字符串" + bodyString);
|
||||
}
|
||||
exchange.getAttributes().put("POST_BODY",bodyString);
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
|
||||
DataBuffer buffer = exchange.getResponse().bufferFactory()
|
||||
.wrap(bytes);
|
||||
return Mono.just(buffer);
|
||||
});
|
||||
|
||||
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
|
||||
exchange.getRequest()) {
|
||||
@Override
|
||||
public Flux<DataBuffer> getBody() {
|
||||
return cachedFlux;
|
||||
}
|
||||
};
|
||||
return chain.filter(exchange.mutate().request(mutatedRequest)
|
||||
.build());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 设置用户信息到请求
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder()
|
||||
{
|
||||
return -200;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 文字脱敏方法
|
||||
* @param bodyString 获取的请求参数jsonString
|
||||
*/
|
||||
private String worldFilter(String bodyString) {
|
||||
log.info("替换符:" + worldWhiteProperties.getSymbol());
|
||||
log.info(bodyString);
|
||||
//创建敏感词库
|
||||
SensitiveWordLibrary sensitiveWordLibrary = new SensitiveWordLibrary();
|
||||
//加载敏感词库
|
||||
try {
|
||||
SensitiveWordLoader.load(sensitiveWordLibrary);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error("加载敏感词库失败,过滤失效:" + e.getMessage());
|
||||
}
|
||||
//分词
|
||||
String[] words = TextSegmentation.segment(bodyString);
|
||||
//轮训替换整个json串
|
||||
return exchangeWord(bodyString,sensitiveWordLibrary,words);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换敏感词
|
||||
* @param bodyString 获取的请求参数jsonString
|
||||
* @param sensitiveWordLibrary 敏感词库
|
||||
* @return
|
||||
*/
|
||||
private String exchangeWord(String bodyString,SensitiveWordLibrary sensitiveWordLibrary,String[] words) {
|
||||
for (int i = 0; i < words.length; i++) {
|
||||
String word = words[i];
|
||||
if (sensitiveWordLibrary.isSensitiveWord(word + ",")) {
|
||||
//如果含有敏感词汇,则进行全局替换
|
||||
String symbol = worldWhiteProperties.getSymbol();
|
||||
StringBuilder symbolBuilder = new StringBuilder();
|
||||
int length = word.length();
|
||||
for (int i1 = 0; i1 < length; i1++) {
|
||||
symbolBuilder.append(symbol);
|
||||
}
|
||||
bodyString = bodyString.replaceAll(word, symbolBuilder.toString());
|
||||
words[i] = worldWhiteProperties.getSymbol();
|
||||
//替换完成一个后,在继续进行轮训,直到满足全部过滤
|
||||
exchangeWord(bodyString,sensitiveWordLibrary,words);
|
||||
}
|
||||
}
|
||||
return bodyString;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.mhd.gateway.filter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.mhd.common.core.utils.ServletUtils;
|
||||
|
||||
/**
|
||||
* 黑名单过滤器
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@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(), "请求地址不允许访问");
|
||||
}
|
||||
|
||||
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,87 @@
|
||||
package com.mhd.gateway.filter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 获取body请求数据(解决流不能重复读取问题)
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@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,89 @@
|
||||
package com.mhd.gateway.filter;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.mhd.gateway.config.properties.CaptchaProperties;
|
||||
import com.mhd.gateway.service.ValidateCodeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.mhd.common.core.utils.ServletUtils;
|
||||
import com.mhd.common.core.utils.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* 验证码过滤器
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Component
|
||||
public class ValidateCodeFilter extends AbstractGatewayFilterFactory<Object>
|
||||
{
|
||||
private final static String[] VALIDATE_URL = new String[] { "/auth/login","/auth/loginApi","/login","/tokenApi/login","/auth/register","/basicApi/findAgreement","/approvalDocumentApi/dingdingHuiDiao" };
|
||||
|
||||
@Autowired
|
||||
private ValidateCodeService validateCodeService;
|
||||
|
||||
@Autowired
|
||||
private CaptchaProperties captchaProperties;
|
||||
|
||||
private static final String CODE = "code";
|
||||
|
||||
private static final String UUID = "uuid";
|
||||
|
||||
private static final String VERIFICATION_CODE = "verificationCode";
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(Object config)
|
||||
{
|
||||
return (exchange, chain) -> {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
|
||||
// 非登录/注册请求或验证码关闭,不处理
|
||||
if (!StringUtils.equalsAnyIgnoreCase(request.getURI().getPath(), VALIDATE_URL) || !captchaProperties.getEnabled())
|
||||
{
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
String rspStr = resolveBodyFromRequest(request);
|
||||
JSONObject obj = JSON.parseObject(rspStr);
|
||||
if (StrUtil.equals(request.getURI().getPath(), "/auth/loginApi") && StrUtil.isNotEmpty(obj.getString(VERIFICATION_CODE))) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
// if (StrUtil.equals(request.getURI().getPath(), "/auth/dingloginApi") && StrUtil.isNotEmpty(obj.getString(VERIFICATION_CODE))) {
|
||||
// return chain.filter(exchange);
|
||||
// }
|
||||
validateCodeService.checkCaptcha(obj.getString(CODE), obj.getString(UUID));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), e.getMessage());
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
};
|
||||
}
|
||||
|
||||
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest)
|
||||
{
|
||||
// 获取请求体
|
||||
Flux<DataBuffer> body = serverHttpRequest.getBody();
|
||||
AtomicReference<String> bodyRef = new AtomicReference<>();
|
||||
body.subscribe(buffer -> {
|
||||
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
|
||||
DataBufferUtils.release(buffer);
|
||||
bodyRef.set(charBuffer.toString());
|
||||
});
|
||||
return bodyRef.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.mhd.gateway.filter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import com.mhd.gateway.config.properties.XssProperties;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import com.mhd.common.core.utils.StringUtils;
|
||||
import com.mhd.common.core.utils.html.EscapeUtil;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 跨站脚本过滤器
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Component
|
||||
@ConditionalOnProperty(value = "security.xss.enabled", havingValue = "true")
|
||||
public class XssFilter implements GlobalFilter, Ordered
|
||||
{
|
||||
// 跨站脚本的 xss 配置,nacos自行添加
|
||||
@Autowired
|
||||
private XssProperties xss;
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
|
||||
{
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
// GET DELETE 不过滤
|
||||
HttpMethod method = request.getMethod();
|
||||
if (method == null || method == HttpMethod.GET || method == HttpMethod.DELETE)
|
||||
{
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
// 非json类型,不过滤
|
||||
if (!isJsonRequest(exchange))
|
||||
{
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
// excludeUrls 不过滤
|
||||
String url = request.getURI().getPath();
|
||||
if (StringUtils.matches(url, xss.getExcludeUrls()))
|
||||
{
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
ServerHttpRequestDecorator httpRequestDecorator = requestDecorator(exchange);
|
||||
return chain.filter(exchange.mutate().request(httpRequestDecorator).build());
|
||||
|
||||
}
|
||||
|
||||
private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange)
|
||||
{
|
||||
ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest())
|
||||
{
|
||||
@Override
|
||||
public Flux<DataBuffer> getBody()
|
||||
{
|
||||
Flux<DataBuffer> body = super.getBody();
|
||||
return body.buffer().map(dataBuffers -> {
|
||||
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
|
||||
DataBuffer join = dataBufferFactory.join(dataBuffers);
|
||||
byte[] content = new byte[join.readableByteCount()];
|
||||
join.read(content);
|
||||
DataBufferUtils.release(join);
|
||||
String bodyStr = new String(content, StandardCharsets.UTF_8);
|
||||
// 防xss攻击过滤
|
||||
bodyStr = EscapeUtil.clean(bodyStr);
|
||||
// 转成字节
|
||||
byte[] bytes = bodyStr.getBytes();
|
||||
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
|
||||
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
|
||||
buffer.write(bytes);
|
||||
return buffer;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getHeaders()
|
||||
{
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.putAll(super.getHeaders());
|
||||
// 由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length
|
||||
httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
|
||||
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
};
|
||||
return serverHttpRequestDecorator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是Json请求
|
||||
*
|
||||
* @param exchange HTTP请求
|
||||
*/
|
||||
public boolean isJsonRequest(ServerWebExchange exchange)
|
||||
{
|
||||
String header = exchange.getRequest().getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
|
||||
return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder()
|
||||
{
|
||||
return -100;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.mhd.gateway.handler;
|
||||
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||
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 com.mhd.common.core.utils.ServletUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 网关统一异常处理
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@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 = "服务未找到";
|
||||
}
|
||||
else if (ex instanceof ResponseStatusException)
|
||||
{
|
||||
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
|
||||
msg = responseStatusException.getMessage();
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = "内部服务器错误";
|
||||
}
|
||||
|
||||
log.error("[网关异常处理]请求路径:{},异常信息:{}", exchange.getRequest().getPath(), ex.getMessage());
|
||||
|
||||
return ServletUtils.webFluxResponseWriter(response, msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.mhd.gateway.handler;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.mhd.common.core.utils.ServletUtils;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebExceptionHandler;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 自定义限流异常处理
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
public class SentinelFallbackHandler implements WebExceptionHandler
|
||||
{
|
||||
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange)
|
||||
{
|
||||
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "请求超过最大数,请稍候再试");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex)
|
||||
{
|
||||
if (exchange.getResponse().isCommitted())
|
||||
{
|
||||
return Mono.error(ex);
|
||||
}
|
||||
if (!BlockException.isBlockException(ex))
|
||||
{
|
||||
return Mono.error(ex);
|
||||
}
|
||||
return handleBlockedRequest(exchange, ex).flatMap(response -> writeResponse(response, exchange));
|
||||
}
|
||||
|
||||
private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable)
|
||||
{
|
||||
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.mhd.gateway.handler;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
import springfox.documentation.swagger.web.SecurityConfiguration;
|
||||
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
|
||||
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
|
||||
import springfox.documentation.swagger.web.UiConfiguration;
|
||||
import springfox.documentation.swagger.web.UiConfigurationBuilder;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/swagger-resources")
|
||||
public class SwaggerHandler
|
||||
{
|
||||
@Autowired(required = false)
|
||||
private SecurityConfiguration securityConfiguration;
|
||||
|
||||
@Autowired(required = false)
|
||||
private UiConfiguration uiConfiguration;
|
||||
|
||||
private final SwaggerResourcesProvider swaggerResources;
|
||||
|
||||
@Autowired
|
||||
public SwaggerHandler(SwaggerResourcesProvider swaggerResources)
|
||||
{
|
||||
this.swaggerResources = swaggerResources;
|
||||
}
|
||||
|
||||
@GetMapping("/configuration/security")
|
||||
public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration()
|
||||
{
|
||||
return Mono.just(new ResponseEntity<>(
|
||||
Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()),
|
||||
HttpStatus.OK));
|
||||
}
|
||||
|
||||
@GetMapping("/configuration/ui")
|
||||
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration()
|
||||
{
|
||||
return Mono.just(new ResponseEntity<>(
|
||||
Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@GetMapping("")
|
||||
public Mono<ResponseEntity> swaggerResources()
|
||||
{
|
||||
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.mhd.gateway.handler;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.mhd.gateway.service.ValidateCodeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import com.mhd.common.core.exception.CaptchaException;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* 验证码获取
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Component
|
||||
public class ValidateCodeHandler implements HandlerFunction<ServerResponse>
|
||||
{
|
||||
@Autowired
|
||||
private ValidateCodeService validateCodeService;
|
||||
|
||||
@Override
|
||||
public Mono<ServerResponse> handle(ServerRequest serverRequest)
|
||||
{
|
||||
AjaxResult ajax;
|
||||
try
|
||||
{
|
||||
ajax = validateCodeService.createCaptcha();
|
||||
}
|
||||
catch (CaptchaException | IOException e)
|
||||
{
|
||||
return Mono.error(e);
|
||||
}
|
||||
return ServerResponse.status(HttpStatus.OK).body(BodyInserters.fromValue(ajax));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.mhd.gateway.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import com.mhd.common.core.exception.CaptchaException;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
|
||||
/**
|
||||
* 验证码处理
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
public interface ValidateCodeService
|
||||
{
|
||||
/**
|
||||
* 生成验证码
|
||||
*/
|
||||
public AjaxResult createCaptcha() throws IOException, CaptchaException;
|
||||
|
||||
/**
|
||||
* 校验验证码
|
||||
*/
|
||||
public void checkCaptcha(String key, String value) throws CaptchaException;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.mhd.gateway.service.impl;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import com.mhd.gateway.service.ValidateCodeService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.FastByteArrayOutputStream;
|
||||
import com.google.code.kaptcha.Producer;
|
||||
import com.mhd.common.core.constant.Constants;
|
||||
import com.mhd.common.core.exception.CaptchaException;
|
||||
import com.mhd.common.core.utils.StringUtils;
|
||||
import com.mhd.common.core.utils.sign.Base64;
|
||||
import com.mhd.common.core.utils.uuid.IdUtils;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import com.mhd.common.redis.service.RedisService;
|
||||
import com.mhd.gateway.config.properties.CaptchaProperties;
|
||||
|
||||
/**
|
||||
* 验证码实现处理
|
||||
*
|
||||
* @author mhd
|
||||
*/
|
||||
@Service
|
||||
public class ValidateCodeServiceImpl implements ValidateCodeService
|
||||
{
|
||||
@Resource(name = "captchaProducer")
|
||||
private Producer captchaProducer;
|
||||
|
||||
@Resource(name = "captchaProducerMath")
|
||||
private Producer captchaProducerMath;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
@Autowired
|
||||
private CaptchaProperties captchaProperties;
|
||||
|
||||
/**
|
||||
* 生成验证码
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult createCaptcha() throws IOException, CaptchaException
|
||||
{
|
||||
AjaxResult ajax = AjaxResult.success();
|
||||
boolean captchaEnabled = captchaProperties.getEnabled();
|
||||
ajax.put("captchaEnabled", captchaEnabled);
|
||||
if (!captchaEnabled)
|
||||
{
|
||||
return ajax;
|
||||
}
|
||||
|
||||
// 保存验证码信息
|
||||
String uuid = IdUtils.simpleUUID();
|
||||
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
|
||||
|
||||
String capStr = null, code = null;
|
||||
BufferedImage image = null;
|
||||
|
||||
String captchaType = captchaProperties.getType();
|
||||
// 生成验证码
|
||||
if ("math".equals(captchaType))
|
||||
{
|
||||
String capText = captchaProducerMath.createText();
|
||||
capStr = capText.substring(0, capText.lastIndexOf("@"));
|
||||
code = capText.substring(capText.lastIndexOf("@") + 1);
|
||||
image = captchaProducerMath.createImage(capStr);
|
||||
}
|
||||
else if ("char".equals(captchaType))
|
||||
{
|
||||
capStr = code = captchaProducer.createText();
|
||||
image = captchaProducer.createImage(capStr);
|
||||
}
|
||||
|
||||
redisService.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
|
||||
// 转换流信息写出
|
||||
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
|
||||
try
|
||||
{
|
||||
ImageIO.write(image, "jpg", os);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
|
||||
ajax.put("uuid", uuid);
|
||||
ajax.put("img", Base64.encode(os.toByteArray()));
|
||||
return ajax;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验验证码
|
||||
*/
|
||||
@Override
|
||||
public void checkCaptcha(String code, String uuid) throws CaptchaException
|
||||
{
|
||||
if (StringUtils.isEmpty(code))
|
||||
{
|
||||
throw new CaptchaException("验证码不能为空");
|
||||
}
|
||||
if (StringUtils.isEmpty(uuid))
|
||||
{
|
||||
throw new CaptchaException("验证码已失效");
|
||||
}
|
||||
String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
|
||||
String captcha = redisService.getCacheObject(verifyKey);
|
||||
redisService.deleteObject(verifyKey);
|
||||
|
||||
if (!code.equalsIgnoreCase(captcha))
|
||||
{
|
||||
throw new CaptchaException("验证码错误");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.mhd.gateway.uilts;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 构建敏感词库
|
||||
*/
|
||||
public class SensitiveWordLibrary {
|
||||
|
||||
private Set<String> sensitiveWords;
|
||||
|
||||
public SensitiveWordLibrary() {
|
||||
sensitiveWords = new HashSet<>();
|
||||
}
|
||||
|
||||
public void addSensitiveWord(String word) {
|
||||
sensitiveWords.add(word);
|
||||
}
|
||||
|
||||
public boolean isSensitiveWord(String word) {
|
||||
return sensitiveWords.contains(word);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mhd.gateway.uilts;
|
||||
|
||||
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* 加载敏感词库
|
||||
*/
|
||||
public class SensitiveWordLoader {
|
||||
|
||||
|
||||
public static void load(SensitiveWordLibrary library) throws IOException {
|
||||
URL resource = SensitiveWordLoader.class.getClassLoader().getResource("disabledWord/politics.txt");
|
||||
if(null == resource){
|
||||
throw new IOException("加载敏感词库失败");
|
||||
}
|
||||
String path = resource.getPath();
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
library.addSensitiveWord(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.mhd.gateway.uilts;
|
||||
|
||||
|
||||
import org.ansj.domain.Result;
|
||||
import org.ansj.splitWord.analysis.NlpAnalysis;
|
||||
|
||||
/**
|
||||
* 待检测文本分词
|
||||
*/
|
||||
public class TextSegmentation {
|
||||
|
||||
public static String[] segment(String text) {
|
||||
Result result = NlpAnalysis.parse(text);
|
||||
return result.getTerms().stream()
|
||||
.map(term -> term.getName())
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
Spring Boot Version: ${spring-boot.version}
|
||||
Spring Application Name: ${spring.application.name}
|
||||
_ _
|
||||
(_) | |
|
||||
_ __ _ _ ___ _ _ _ ______ __ _ __ _ | |_ ___ __ __ __ _ _ _
|
||||
| '__|| | | | / _ \ | | | || ||______| / _` | / _` || __| / _ \\ \ /\ / / / _` || | | |
|
||||
| | | |_| || (_) || |_| || | | (_| || (_| || |_ | __/ \ V V / | (_| || |_| |
|
||||
|_| \__,_| \___/ \__, ||_| \__, | \__,_| \__| \___| \_/\_/ \__,_| \__, |
|
||||
__/ | __/ | __/ |
|
||||
|___/ |___/ |___/
|
||||
@@ -0,0 +1,35 @@
|
||||
# Tomcat
|
||||
server:
|
||||
port: 8088
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: mhd-gateway
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: dev
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
#server-addr: 192.168.0.69:8848
|
||||
server-addr: 127.0.0.1:8848
|
||||
#server-addr: 116.204.23.92:8848
|
||||
namespace: 4dd3256a-22c0-42db-9af1-de513a05c6a9
|
||||
config:
|
||||
# 配置中心地址
|
||||
#server-addr: 192.168.0.69:8848
|
||||
server-addr: 127.0.0.1:8848
|
||||
#server-addr: 116.204.23.92:8848
|
||||
# 锁定server端的配置文件(读取它的配置项)
|
||||
namespace: 4dd3256a-22c0-42db-9af1-de513a05c6a9
|
||||
group: DEFAULT_GROUP # 默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置
|
||||
file-extension: yml #默认properties
|
||||
zipkin:
|
||||
base-url: http://192.168.0.69:9411 # zipkin server的请求地址
|
||||
sender:
|
||||
# web 客户端将踪迹日志数据通过网络请求的方式传送到服务端,另外还有配置
|
||||
# kafka/rabbit 客户端将踪迹日志数据传递到mq进行中转
|
||||
type: web
|
||||
|
||||
@@ -0,0 +1,631 @@
|
||||
习近平,
|
||||
平近习,
|
||||
xjp,
|
||||
习太子,
|
||||
习明泽,
|
||||
老习,
|
||||
温家宝,
|
||||
温加宝,
|
||||
温x,
|
||||
温jia宝,
|
||||
温宝宝,
|
||||
温加饱,
|
||||
温加保,
|
||||
张培莉,
|
||||
温云松,
|
||||
温如春,
|
||||
温jb,
|
||||
胡温,
|
||||
胡x,
|
||||
胡jt,
|
||||
胡boss,
|
||||
胡总,
|
||||
胡王八,
|
||||
hujintao,
|
||||
胡jintao,
|
||||
胡j涛,
|
||||
胡惊涛,
|
||||
胡景涛,
|
||||
胡紧掏,
|
||||
湖紧掏,
|
||||
胡紧套,
|
||||
锦涛,
|
||||
hjt,
|
||||
胡派,
|
||||
胡主席,
|
||||
刘永清,
|
||||
胡海峰,
|
||||
胡海清,
|
||||
江泽民,
|
||||
民泽江,
|
||||
江胡,
|
||||
江哥,
|
||||
江主席,
|
||||
江书记,
|
||||
江浙闽,
|
||||
江沢民,
|
||||
江浙民,
|
||||
择民,
|
||||
则民,
|
||||
茳泽民,
|
||||
zemin,
|
||||
ze民,
|
||||
老江,
|
||||
老j,
|
||||
江core,
|
||||
江x,
|
||||
江派,
|
||||
江zm,
|
||||
jzm,
|
||||
江戏子,
|
||||
江蛤蟆,
|
||||
江某某,
|
||||
江贼,
|
||||
江猪,
|
||||
江氏集团,
|
||||
江绵恒,
|
||||
江绵康,
|
||||
王冶坪,
|
||||
江泽慧,
|
||||
邓小平,
|
||||
平小邓,
|
||||
xiao平,
|
||||
邓xp,
|
||||
邓晓平,
|
||||
邓朴方,
|
||||
邓榕,
|
||||
邓质方,
|
||||
毛泽东,
|
||||
猫泽东,
|
||||
猫则东,
|
||||
猫贼洞,
|
||||
毛zd,
|
||||
毛zx,
|
||||
z东,
|
||||
ze东,
|
||||
泽d,
|
||||
zedong,
|
||||
毛太祖,
|
||||
毛相,
|
||||
主席画像,
|
||||
改革历程,
|
||||
朱镕基,
|
||||
朱容基,
|
||||
朱镕鸡,
|
||||
朱容鸡,
|
||||
朱云来,
|
||||
李鹏,
|
||||
李peng,
|
||||
里鹏,
|
||||
李月月鸟,
|
||||
李小鹏,
|
||||
李小琳,
|
||||
华主席,
|
||||
华国,
|
||||
国锋,
|
||||
国峰,
|
||||
锋同志,
|
||||
白春礼,
|
||||
薄熙来,
|
||||
薄一波,
|
||||
蔡赴朝,
|
||||
蔡武,
|
||||
曹刚川,
|
||||
常万全,
|
||||
陈炳德,
|
||||
陈德铭,
|
||||
陈建国,
|
||||
陈良宇,
|
||||
陈绍基,
|
||||
陈同海,
|
||||
陈至立,
|
||||
戴秉国,
|
||||
丁一平,
|
||||
董建华,
|
||||
杜德印,
|
||||
杜世成,
|
||||
傅锐,
|
||||
郭伯雄,
|
||||
郭金龙,
|
||||
贺国强,
|
||||
胡春华,
|
||||
耀邦,
|
||||
华建敏,
|
||||
黄华华,
|
||||
黄丽满,
|
||||
黄兴国,
|
||||
回良玉,
|
||||
贾庆林,
|
||||
贾廷安,
|
||||
靖志远,
|
||||
李长春,
|
||||
李春城,
|
||||
李建国,
|
||||
李克强,
|
||||
李岚清,
|
||||
李沛瑶,
|
||||
李荣融,
|
||||
李瑞环,
|
||||
李铁映,
|
||||
李先念,
|
||||
李学举,
|
||||
李源潮,
|
||||
栗智,
|
||||
梁光烈,
|
||||
廖锡龙,
|
||||
林树森,
|
||||
林炎志,
|
||||
林左鸣,
|
||||
令计划,
|
||||
柳斌杰,
|
||||
刘奇葆,
|
||||
刘少奇,
|
||||
刘延东,
|
||||
刘云山,
|
||||
刘志军,
|
||||
龙新民,
|
||||
路甬祥,
|
||||
罗箭,
|
||||
吕祖善,
|
||||
马飚,
|
||||
马恺,
|
||||
孟建柱,
|
||||
欧广源,
|
||||
强卫,
|
||||
沈跃跃,
|
||||
宋平顺,
|
||||
粟戎生,
|
||||
苏树林,
|
||||
孙家正,
|
||||
铁凝,
|
||||
屠光绍,
|
||||
王东明,
|
||||
汪东兴,
|
||||
王鸿举,
|
||||
王沪宁,
|
||||
王乐泉,
|
||||
王洛林,
|
||||
王岐山,
|
||||
王胜俊,
|
||||
王太华,
|
||||
王学军,
|
||||
王兆国,
|
||||
王振华,
|
||||
吴邦国,
|
||||
吴定富,
|
||||
吴官正,
|
||||
无官正,
|
||||
吴胜利,
|
||||
吴仪,
|
||||
奚国华,
|
||||
习仲勋,
|
||||
徐才厚,
|
||||
许其亮,
|
||||
徐绍史,
|
||||
杨洁篪,
|
||||
叶剑英,
|
||||
由喜贵,
|
||||
于幼军,
|
||||
俞正声,
|
||||
袁纯清,
|
||||
曾培炎,
|
||||
曾庆红,
|
||||
曾宪梓,
|
||||
曾荫权,
|
||||
张德江,
|
||||
张定发,
|
||||
张高丽,
|
||||
张立昌,
|
||||
张荣坤,
|
||||
张志国,
|
||||
赵洪祝,
|
||||
紫阳,
|
||||
周生贤,
|
||||
周永康,
|
||||
朱海仑,
|
||||
中南海,
|
||||
大陆当局,
|
||||
中国当局,
|
||||
北京当局,
|
||||
共产党,
|
||||
党产共,
|
||||
共贪党,
|
||||
阿共,
|
||||
产党共,
|
||||
公产党,
|
||||
工产党,
|
||||
共c党,
|
||||
共x党,
|
||||
共铲,
|
||||
供产,
|
||||
共惨,
|
||||
供铲党,
|
||||
供铲谠,
|
||||
供铲裆,
|
||||
共残党,
|
||||
共残主义,
|
||||
共产主义的幽灵,
|
||||
拱铲,
|
||||
老共,
|
||||
中共,
|
||||
中珙,
|
||||
中gong,
|
||||
gc党,
|
||||
贡挡,
|
||||
gong党,
|
||||
g产,
|
||||
狗产蛋,
|
||||
共残裆,
|
||||
恶党,
|
||||
邪党,
|
||||
共产专制,
|
||||
共产王朝,
|
||||
裆中央,
|
||||
土共,
|
||||
土g,
|
||||
共狗,
|
||||
g匪,
|
||||
共匪,
|
||||
仇共,
|
||||
政府,
|
||||
症腐,
|
||||
政腐,
|
||||
政付,
|
||||
正府,
|
||||
政俯,
|
||||
政f,
|
||||
zhengfu,
|
||||
政zhi,
|
||||
挡中央,
|
||||
档中央,
|
||||
中央领导,
|
||||
中国zf,
|
||||
中央zf,
|
||||
国wu院,
|
||||
中华帝国,
|
||||
gong和,
|
||||
大陆官方,
|
||||
北京政权,
|
||||
江泽民,
|
||||
胡锦涛,
|
||||
温家宝,
|
||||
习近平,
|
||||
习仲勋,
|
||||
贺国强,
|
||||
贺子珍,
|
||||
周永康,
|
||||
李长春,
|
||||
李德生,
|
||||
王岐山,
|
||||
姚依林,
|
||||
回良玉,
|
||||
李源潮,
|
||||
李干成,
|
||||
戴秉国,
|
||||
黄镇,
|
||||
刘延东,
|
||||
刘瑞龙,
|
||||
俞正声,
|
||||
黄敬,
|
||||
薄熙,
|
||||
薄一波,
|
||||
周小川,
|
||||
周建南,
|
||||
温云松,
|
||||
徐明,
|
||||
江泽慧,
|
||||
江绵恒,
|
||||
江绵康,
|
||||
李小鹏,
|
||||
李鹏,
|
||||
李小琳,
|
||||
朱云来,
|
||||
朱容基,
|
||||
法轮功,
|
||||
李洪志,
|
||||
新疆骚乱,
|
||||
爱液,
|
||||
按摩棒,
|
||||
拔出来,
|
||||
爆草,
|
||||
包二奶,
|
||||
暴干,
|
||||
暴奸,
|
||||
暴乳,
|
||||
爆乳,
|
||||
暴淫,
|
||||
被操,
|
||||
被插,
|
||||
被干,
|
||||
逼奸,
|
||||
仓井空,
|
||||
插暴,
|
||||
操逼,
|
||||
操黑,
|
||||
操烂,
|
||||
肏你,
|
||||
肏死,
|
||||
操死,
|
||||
操我,
|
||||
厕奴,
|
||||
插比,
|
||||
插b,
|
||||
插逼,
|
||||
插进,
|
||||
插你,
|
||||
插我,
|
||||
插阴,
|
||||
潮吹,
|
||||
潮喷,
|
||||
成人电影,
|
||||
成人论坛,
|
||||
成人色情,
|
||||
成人网站,
|
||||
成人文学,
|
||||
成人小说,
|
||||
艳情小说,
|
||||
成人游戏,
|
||||
吃精,
|
||||
抽插,
|
||||
春药,
|
||||
大波,
|
||||
大力抽送,
|
||||
大乳,
|
||||
荡妇,
|
||||
荡女,
|
||||
盗撮,
|
||||
发浪,
|
||||
放尿,
|
||||
肥逼,
|
||||
粉穴,
|
||||
风月大陆,
|
||||
干死你,
|
||||
干穴,
|
||||
肛交,
|
||||
肛门,
|
||||
龟头,
|
||||
裹本,
|
||||
国产av,
|
||||
好嫩,
|
||||
豪乳,
|
||||
黑逼,
|
||||
后庭,
|
||||
后穴,
|
||||
虎骑,
|
||||
换妻俱乐部,
|
||||
黄片,
|
||||
几吧,
|
||||
鸡吧,
|
||||
鸡巴,
|
||||
鸡奸,
|
||||
妓女,
|
||||
奸情,
|
||||
叫床,
|
||||
脚交,
|
||||
精液,
|
||||
就去日,
|
||||
巨屌,
|
||||
菊花洞,
|
||||
菊门,
|
||||
巨奶,
|
||||
巨乳,
|
||||
菊穴,
|
||||
开苞,
|
||||
口爆,
|
||||
口活,
|
||||
口交,
|
||||
口射,
|
||||
口淫,
|
||||
裤袜,
|
||||
狂操,
|
||||
狂插,
|
||||
浪逼,
|
||||
浪妇,
|
||||
浪叫,
|
||||
浪女,
|
||||
狼友,
|
||||
聊性,
|
||||
凌辱,
|
||||
漏乳,
|
||||
露b,
|
||||
乱交,
|
||||
乱伦,
|
||||
轮暴,
|
||||
轮操,
|
||||
轮奸,
|
||||
裸陪,
|
||||
买春,
|
||||
美逼,
|
||||
美少妇,
|
||||
美乳,
|
||||
美腿,
|
||||
美穴,
|
||||
美幼,
|
||||
秘唇,
|
||||
迷奸,
|
||||
密穴,
|
||||
蜜穴,
|
||||
蜜液,
|
||||
摸奶,
|
||||
摸胸,
|
||||
母奸,
|
||||
奈美,
|
||||
奶子,
|
||||
男奴,
|
||||
内射,
|
||||
嫩逼,
|
||||
嫩女,
|
||||
嫩穴,
|
||||
捏弄,
|
||||
女优,
|
||||
炮友,
|
||||
砲友,
|
||||
喷精,
|
||||
屁眼,
|
||||
前凸后翘,
|
||||
强jian,
|
||||
强暴,
|
||||
强奸处女,
|
||||
情趣用品,
|
||||
情色,
|
||||
拳交,
|
||||
全裸,
|
||||
群交,
|
||||
人妻,
|
||||
人兽,
|
||||
日逼,
|
||||
日烂,
|
||||
肉棒,
|
||||
肉逼,
|
||||
肉唇,
|
||||
肉洞,
|
||||
肉缝,
|
||||
肉棍,
|
||||
肉茎,
|
||||
肉具,
|
||||
揉乳,
|
||||
肉穴,
|
||||
肉欲,
|
||||
乳爆,
|
||||
乳房,
|
||||
乳沟,
|
||||
乳交,
|
||||
乳头,
|
||||
骚逼,
|
||||
骚比,
|
||||
骚女,
|
||||
骚水,
|
||||
骚穴,
|
||||
色逼,
|
||||
色界,
|
||||
色猫,
|
||||
色盟,
|
||||
色情网站,
|
||||
色区,
|
||||
色色,
|
||||
色诱,
|
||||
色欲,
|
||||
色b,
|
||||
少年阿宾,
|
||||
射爽,
|
||||
射颜,
|
||||
食精,
|
||||
释欲,
|
||||
兽奸,
|
||||
兽交,
|
||||
手淫,
|
||||
兽欲,
|
||||
熟妇,
|
||||
熟母,
|
||||
熟女,
|
||||
爽片,
|
||||
双臀,
|
||||
死逼,
|
||||
丝袜,
|
||||
丝诱,
|
||||
松岛枫,
|
||||
酥痒,
|
||||
汤加丽,
|
||||
套弄,
|
||||
体奸,
|
||||
体位,
|
||||
舔脚,
|
||||
舔阴,
|
||||
调教,
|
||||
偷欢,
|
||||
推油,
|
||||
脱内裤,
|
||||
文做,
|
||||
舞女,
|
||||
无修正,
|
||||
吸精,
|
||||
夏川纯,
|
||||
相奸,
|
||||
小逼,
|
||||
校鸡,
|
||||
小穴,
|
||||
小xue,
|
||||
性感妖娆,
|
||||
性感诱惑,
|
||||
性虎,
|
||||
性饥渴,
|
||||
性技巧,
|
||||
性交,
|
||||
性奴,
|
||||
性虐,
|
||||
性息,
|
||||
性欲,
|
||||
胸推,
|
||||
穴口,
|
||||
穴图,
|
||||
亚情,
|
||||
颜射,
|
||||
阳具,
|
||||
杨思敏,
|
||||
要射了,
|
||||
夜勤病栋,
|
||||
一本道,
|
||||
一夜欢,
|
||||
一夜情,
|
||||
一ye情,
|
||||
阴部,
|
||||
淫虫,
|
||||
阴唇,
|
||||
淫荡,
|
||||
阴道,
|
||||
淫电影,
|
||||
阴阜,
|
||||
淫妇,
|
||||
淫河,
|
||||
阴核,
|
||||
阴户,
|
||||
淫贱,
|
||||
淫叫,
|
||||
淫教师,
|
||||
阴茎,
|
||||
阴精,
|
||||
淫浪,
|
||||
淫媚,
|
||||
淫糜,
|
||||
淫魔,
|
||||
淫母,
|
||||
淫女,
|
||||
淫虐,
|
||||
淫妻,
|
||||
淫情,
|
||||
淫色,
|
||||
淫声浪语,
|
||||
淫兽学园,
|
||||
淫书,
|
||||
淫术炼金士,
|
||||
淫水,
|
||||
淫娃,
|
||||
淫威,
|
||||
淫亵,
|
||||
淫样,
|
||||
淫液,
|
||||
淫照,
|
||||
阴b,
|
||||
应召,
|
||||
幼交,
|
||||
欲火,
|
||||
欲女,
|
||||
玉乳,
|
||||
玉穴,
|
||||
援交,
|
||||
原味内衣,
|
||||
援助交际,
|
||||
招鸡,
|
||||
招妓,
|
||||
抓胸,
|
||||
自慰,
|
||||
作爱,
|
||||
a片,
|
||||
fuck,
|
||||
gay片,
|
||||
g点,
|
||||
h动画,
|
||||
h动漫,
|
||||
失身粉,
|
||||
淫荡自慰器,
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="logs/mhd-gateway" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 系统日志输出 -->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/info.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>INFO</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>ERROR</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 系统模块日志级别控制 -->
|
||||
<logger name="com.mhd" level="info" />
|
||||
<!-- Spring日志级别控制 -->
|
||||
<logger name="org.springframework" level="warn" />
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info" />
|
||||
<appender-ref ref="file_error" />
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user