SpringCloudGateWay

SpringCloudGateWay进行服务化:

我们需要将我们的springCloudGatewa项目当作一个服务注册到EurekaServer中,配置如下

server:

port: 8888

spring:

application:

name: cloud-gateway-eureka

cloud:

gateway:

discovery:

locator:

enabled: true

eureka:

client:

service-url:

defaultZone: http://localhost:8000/eureka/

logging:

level:

org.springframework.cloud.gateway: debug

spring.cloud.gateway.discovery.locator.enable是否与服务注册于发现组件进行结合,通过注册于Eureka server的serviceId匹配转发到对应的服务实例,从而根据serviceId创建路由功能。

Logging.level.org.springframework.cloud.gateway日志级别,便于排查问题

此时可以在http://localhost:8000/上看到注册的serviceId为CLOUD-GATEWAY-EUREKA("在EurekaServer自动转换成大写")的服务。

通过网关访问注册在EurekaServer上的服务的语法:

http://网关地址:端口/服务中心注册 serviceId/具体的url

比如和我们的网关服务注册在同一个EurekaServer上还注册了一个serviceId为"PRODUCER"的服务, 同时我们的网关服务开启了spring.cloud.gateway.discovery.locator.enable,即spring.cloud.gateway.discovery.locator.enable:true,比如我们的网关服务器ip是192.168.10.1,端口是8080,我们的PRODUCER服务的主机IP是192.168.10.2,端口是:9999如果我们想通过网关访问我们的PRODUCER服务的say功能,我们可以键入请求:http://192.168.10.1:8080/PRODUCER/say这样请求就会被网关转发,相当于我们直接请求http://192.168.10.2:9999/say

相比较于我们直接访问我们的服务,我们可以通过网关服务统一对所有调用我们任意服务的请求进行过滤路由:

这就需要我们设置路由规则:

例如下面的路由规则

spring:

cloud:

gateway:

routes:

  • id: nameRoot

uri: http://nameservice

predicates:

  • Path=/name/**

filters:

  • StripPrefix=2

  • id: nameTree

uri: lb://spring-cloud-producer

predicates:

  • Mathod=get

  • Path=/poing/**

Filters:

  • PrefixPath=/bing

从上面的例子我们可以看到我们需要在spring.clou.gateway.routes下配置路由规则,

包括id uri predicates filter字段,

其中id字段为路由规则id,

uri字段是在predicates断言为true时路由的地址,在id为nameTree的路由规则我们可以看到用到了lb://spring-cloud-producer而不是直接指定的新的地址,这里的lb是一种协议,gateway将使用LoadBaLancerClient把spring-cloud-producer通过Eureka解析serviceId为spring-cloud-producer对应的主机和端口进行负载均衡.

当我们的uri:使用的是http或者https时使用的是netty的HttpClient进行路由转发

当我们的uri:使用的时ws:或者wss:时使用的是Spring web socket进行路由转发,如果我们需要路由转发同时还要负载均衡话,我们可以在uri:里面同时设置使用lb:ws:serviceId这样既能实现转发也能实现负载

predicates后面跟的条件,与请求进行匹配从而断言是否满足,filter后面跟的是各种filter 是org.springframework.cloud.gateway.filter.GatewayFilter的实例

Predicate来源于java8的函数编程,Predicate接受一个入参返回的是布尔类型的参数,内部的defaulte函数支持与或非 test等的操作,详情查看api

SpringCloud内置的集中Predicate的实现

SpringCloudGateway的常见的几种Filter

StriPrefix Filter是一种请求拦截的功能的Filter我们可以使用利用这个来做请求拼接

例如 spring:

cloud:

gateway:

routes:

  • id: nameRoot

uri: http://nameservice

predicates:

  • Path=/name/**

filters:

  • StripPrefix=2

    Server:
    Port:8080

这个的意思就是说当我们匹配到/name/**时,比如我们的请求http://locahost:8080/name/a/b时StriPrefix=2就代表截取的路径的个数,这样gateway在进行路由转发时,就会生成新的转发请求:http://nameservice/b
PrefixPath Filter
PrefixPath Filter的作用和StriPrefix Filter刚好相反,实在转发URL时添加一部分前缀
例如 spring:
cloud:
gateway:
routes:

filters:

  • PrefixPath=/mypath

Server:

Port:8080

这样当你请求为http://localhost:8080/name/a/b 这样gateway在进行转发时就会转发成新的请求http://nameservice/mypath/name/a/b

如果自定义GateWayFilterFactory可以参考SpringCloudGateWay提供的几种常见的Filter的实现方式,也就是继承抽象类AbstractNameValueGateWayFilterFactory 实现 GateWayFilter apply(C Config)方法,然后加入到容器被容器管理即可

public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthorizeGatewayFilterFactory.Config> {

复制代码
private static final Log logger = LogFactory.getLog(AuthorizeGatewayFilterFactory.class);

private static final String AUTHORIZE_TOKEN = "token";
private static final String AUTHORIZE_UID = "uid";

@Autowired
private StringRedisTemplate stringRedisTemplate;

public AuthorizeGatewayFilterFactory() {
    super(Config.class);
    logger.info("Loaded GatewayFilterFactory [Authorize]");
}

@Override
public List<String> shortcutFieldOrder() {
    return Arrays.asList("enabled");
}

@Override
public GatewayFilter apply(AuthorizeGatewayFilterFactory.Config config) {
    return (exchange, chain) -> {
        if (!config.isEnabled()) {
            return chain.filter(exchange);
        }

        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst(AUTHORIZE_TOKEN);
        String uid = headers.getFirst(AUTHORIZE_UID);
        if (token == null) {
            token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
        }
        if (uid == null) {
            uid = request.getQueryParams().getFirst(AUTHORIZE_UID);
        }

        ServerHttpResponse response = exchange.getResponse();
        if (StringUtils.isEmpty(token) || StringUtils.isEmpty(uid)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        String authToken = stringRedisTemplate.opsForValue().get(uid);
        if (authToken == null || !authToken.equals(token)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        return chain.filter(exchange);
    };
}

public static class Config {
    // 控制是否开启认证
    private boolean enabled;

    public Config() {}

    public boolean isEnabled() {
        return enabled;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }
}

}

如何应用呢

网关路由配置

spring:

cloud:

gateway:

routes:

  • id: user-service

uri: http://localhost:8077/api/user/list

predicates:

  • Path=/user/list

filters:

关键在下面一句,值为true则开启认证,false则不开启

这种配置方式和spring cloud gateway内置的GatewayFilterFactory一致

  • Authorize=true

另外一种自定义GateWayFilter的方式时实现GateWayFilter 和Ordered接口

public class AuthorizeGatewayFilter implements GatewayFilter, Ordered {

复制代码
private static final String AUTHORIZE_TOKEN = "token";
private static final String AUTHORIZE_UID = "uid";

@Autowired
private StringRedisTemplate stringRedisTemplate;

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();
    HttpHeaders headers = request.getHeaders();
    String token = headers.getFirst(AUTHORIZE_TOKEN);
    String uid = headers.getFirst(AUTHORIZE_UID);
    if (token == null) {
        token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
    }
    if (uid == null) {
        uid = request.getQueryParams().getFirst(AUTHORIZE_UID);
    }

    ServerHttpResponse response = exchange.getResponse();
    if (StringUtils.isEmpty(token) || StringUtils.isEmpty(uid)) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }
    String authToken = stringRedisTemplate.opsForValue().get(uid);
    if (authToken == null || !authToken.equals(token)) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }

    return chain.filter(exchange);
}

@Override
public int getOrder() {
    return 0;
}

}

如何使用我们的自定义的AuthorizeGateWayFilter呢,

在我们的RouteLocatorBuilder中添加,当然继承了AbstractNameValueGateWayFilterFactory的自定义filter也可以通过这种方式注册进去生效:

@SpringBootApplication

public class GatewayApplication {

复制代码
public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class, args);
}

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
    return builder.routes().route(r ->
            r.path("/user/list")
                    .uri("http://localhost:8077/api/user/list")
                    .filters(new AuthorizeGatewayFilter())
                    .id("user-service"))
            .build();
}

}

如果我们要定义全局的Filter从而对所有的请求都起过滤作用则需要实现GlobalFilter 和ordered接口并实现对应的方法如下:

@Component

public class AuthorizeFilter implements GlobalFilter, Ordered {

private static final String AUTHORIZE_TOKEN = "token";

private static final String AUTHORIZE_UID = "uid";

复制代码
@Autowired
private StringRedisTemplate stringRedisTemplate;

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    ServerHttpRequest request = exchange.getRequest();
    HttpHeaders headers = request.getHeaders();
    String token = headers.getFirst(AUTHORIZE_TOKEN);
    String uid = headers.getFirst(AUTHORIZE_UID);
    if (token == null) {
        token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);
    }
    if (uid == null) {
        uid = request.getQueryParams().getFirst(AUTHORIZE_UID);
    }

    ServerHttpResponse response = exchange.getResponse();
    if (StringUtils.isEmpty(token) || StringUtils.isEmpty(uid)) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }
    String authToken = stringRedisTemplate.opsForValue().get(uid);
    if (authToken == null || !authToken.equals(token)) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }

    return chain.filter(exchange);
}

@Override
public int getOrder() {
    return 0;
}

}

如何让我们自定义的全局Filter起作用呢

不需要做任何额外的操作只需要对我们自定义的Filter添加@Component注解 让容器管理这样就对所有请求起到过滤作用。

相关推荐
用户83562907805123 分钟前
C# 高效生成 Word 表格:复杂表格创建实战指南
后端·c#
爱泡脚的鸡腿26 分钟前
uni-app D5 实战(小兔鲜)
前端
我是小妖怪,潇洒又自在27 分钟前
springcloud alibaba搭建
后端·spring·spring cloud
tomato_40428 分钟前
本地系统、虚拟机、远程服务器三者之间的核心区别
前端
卫生纸不够用28 分钟前
Appium-锁屏-Android
android·appium
阿拉斯攀登38 分钟前
安卓工控机 OTA 升级方案(SpringBoot+MQTT)
android·spring boot·物联网·iot
回家路上绕了弯41 分钟前
支付请求幂等性设计:从原理到落地,杜绝重复扣款
分布式·后端
iOS开发上架哦1 小时前
APP应用上架完整指南:从准备资料到上架流程详解
后端
许商1 小时前
【stm32】【printf】
java·前端·stm32