一、简介
SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。目标是替代 Zuul,为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。它是一个微服务 它能够整体进行一个鉴权,监控,限流,不用每个微服务都进行这样的重复流程
二、组成
路由:一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。
断言:自定义断言,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
过滤器:全局过滤器(implements GlobalFilter, Ordered)
局部过滤器(extends AbstractGatewayFilterFactory
routes: # 路由数组 当满足什么条件时转发到哪个微服务上
- id: message-gateway # 一般填写微服务的id
uri: http://127.0.0.1:8081 # 请求最终转发地址
# uri: lb://message-core # 要转发到的地址 lb是负载均衡,后面是微服务在nacos上的标识
order: 1 # 优先级
predicates: # 断言,匹配上就进行转发
- Path=/user/test
- Age=18,36
filters: # 对请求做一些处理
- StripPrefix = 1
- Log=true,false
自定义断言
@Component public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory{ public AgeRoutePredicateFactory() { super(AgeRoutePredicateFactory.Config.class); } //读取配置文件中的参数值 给他赋值到配置类中 @Override public List shortcutFieldOrder() { return Arrays.asList("minAge","maxAge"); } //断言逻辑 @Override public Predicate apply(AgeRoutePredicateFactory.Config config) { return new Predicate () { @Override public boolean test(ServerWebExchange serverWebExchange) { //接收前台的参数 String age = serverWebExchange.getRequest().getQueryParams().getFirst("age"); if (StrUtil.isNotBlank(age)) { int i = Integer.parseInt(age); return i < config.getMaxAge() && i > config.getMinAge(); } return false; } }; } //配置类,用于接收配置文件中的对应参数 @Data @NoArgsConstructor public static class Config { private int maxAge; private int minAge; } }
@Component public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory{ public LogGatewayFilterFactory() { super(LogGatewayFilterFactory.Config.class); } //读取配置文件的参数,赋值到配置类中 @Override public List shortcutFieldOrder() { return Arrays.asList("consoleLog", "cacheLog"); } //过滤器逻辑 @Override public GatewayFilter apply(Config config) { return new GatewayFilter() { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { if (config.isCacheLog()) { System.out.println("cache开启了"); } if (config.isConsoleLog()) { System.out.println("控制台开启了"); } return chain.filter(exchange); } }; } //配置类,用于接收配置文件中的对应参数 @Data @NoArgsConstructor public static class Config { private boolean consoleLog; private boolean cacheLog; } }
@Component
@Slf4j
@RequiredArgsConstructor
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private String[] sqlPatternList = {"%'", "select ", "insert ", "delete ", " from",
"count\(", "drop table", "update ", "truncate ", "asc\(",
"mid\(", "char\(", "xp_cmdshell ", "exec ",
"netlocalgroup administrators", "net user", " or ", " and ", "'"};
private String[] paramNamePattern = {"[", "]", "<", ">"};
private AntPathMatcher antPathMatcher = new AntPathMatcher();
private final RedisTemplate redisTemplate;
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//指定编码,否则在浏览器中会中文乱码
exchange.getResponse().getHeaders().add("Content-Type", "application/json;charset=UTF-8");
String hostName = exchange.getRequest().getRemoteAddress().getHostName();
//判断访问的地址是否出现在黑名单
Boolean ip = redisTemplate.opsForSet().isMember("blackIp", hostName);
if (ip) {
return exchange.getResponse().setComplete();
}
//防止sql注入
MultiValueMap queryParams = exchange.getRequest().getQueryParams();
for (String key : queryParams.keySet()) {
//参数名特殊字符判断
for (String item : paramNamePattern) {
if (key.contains(item)) {
return exchange.getResponse().setComplete();
}
}
String param = queryParams.getFirst(key);
if (param != null) {
//sql注入校验
if (valid(param)) {
return exchange.getResponse().setComplete();
}
}
}
// 获取请求头的token
String token = exchange.getRequest().getHeaders().getFirst(JwtUtils.AUTHORIZATIONTOKEN);
if (StrUtil.isBlank(token)) {
log.info("没有携带token,需要进行认证");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
private boolean valid(String value) {
for (String item : sqlPatternList) {
if (value.contains(item)) {
return true;
}
}
return false;
}
}
限流。后续更新
springSecurity认证流程
首先进行账号密码的验证
@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager(SecurityUserDetailService userDetailsService) {
UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService);
authenticationManager.setPasswordEncoder(myPasswordEncoder);
return authenticationManager;
}
进入认证,生成token,并将token存放到redis里,返回给前端
git网址
最后,项目的登录流程简单讲就是
1、用户输入账号密码进行登录后,首先IP会先经过验证是否是黑名单,并输入的数据是否为sql注入,如果是,那么就直接进行拦截 2、通过验证之后,使用springSecurity进行账号密码的验证,验证通过后进行认证,认证后会通过jwt生成token,并发送一个token给前端,并将此token存到redis里
参考链接:https://www.jianshu.com/p/014e0d754bfe



