spring cloud gateway

一、简介
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<LogGatewayFilterFactory.Config>)

      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

自定义断言

/**
 * 自定义断言工厂
 * 参照内置断言工厂写 BeforeRoutePredicateFactory
 * 类的命名是固定的,yml配置了XXX,就需要命名为XXXRoutePredicateFactory
 * 需要自定义配置类,获取配置内容
 */
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {

    public AgeRoutePredicateFactory() {
        super(AgeRoutePredicateFactory.Config.class);
    }

    //读取配置文件中的参数值 给他赋值到配置类中
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("minAge","maxAge");
    }

    //断言逻辑
    @Override
    public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @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;
    }

}
/**
 * 自定义局部过滤器
 * 类名需要规范,XXXGatewayFilterFactory
 * 需要自定义配置类,获取配置内容
 */
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {

    public LogGatewayFilterFactory() {
        super(LogGatewayFilterFactory.Config.class);
    }

    //读取配置文件的参数,赋值到配置类中
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("consoleLog", "cacheLog");
    }

    //过滤器逻辑
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> 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;
    }

}
/**
 * todo 需要防止xss攻击和sql注入问题
 * <p>
 * 自定义全局过滤器 必须实现GlobalFilter, Ordered
 * 作用:统一鉴权,用户是否经过认证都必须进过这一步
 * 经过认证,那么进行请求转发
 * 未经过认证,跳转到security进行认证
 */
@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<String, String> redisTemplate;

    /**
     * 过滤器逻辑,用户发送的请求是否携带token或token是否正确
     * 正确放行,错误返回错误信息
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        /**
         * String path = exchange.getRequest().getURI().getPath();
         if (antPathMatcher.match("mess/**",path)) {
         System.out.println("使用nginx配置时,可能会出现统一的前缀,那么就可以使用这个");
         }
         */

        //指定编码,否则在浏览器中会中文乱码
        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<String, String> 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);
    }

    /**
     * 标识过滤器优先级
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }

    private boolean valid(String value) {
        for (String item : sqlPatternList) {
            if (value.contains(item)) {
                return true;
            }
        }
        return false;
    }
}

限流。后续更新
springSecurity认证流程
首先进行账号密码的验证

/**
     * 验证账号密码
     *
     * @param userDetailsService
     * @return
     */
    @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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值