每天20分钟之spring-cloud-gateway基础四自定义过滤工厂

2022-08-21 22:20:14 浏览数 (2)

过滤器工厂(修改请求)

AddRequestHeader过滤器工厂

代码语言:txt复制
- AddRequestHeader=from,abc

RemoveRequestHeader过滤器工厂

代码语言:txt复制
- RemoveRequestHeader=from2

SetStatus过滤器工厂

代码语言:txt复制
- id: statusFilter
    uri: http://www.example.com/
    predicates:
    - Query=foo,b1
    filters:
    - SetStatus=401

RedirectTo过滤器工厂

代码语言:txt复制
- id: redictFilter
    uri: http://www.example.com/
    predicates:
    - Query=foo,b2
    filters:
    - RedirectTo=302,http://weibo.com

自定义过滤器工厂

  • 自定义过滤器配置
代码语言:txt复制
 - id: lb-info
    uri: lb://consul-server-producer
    predicates:
    - Path=/info/**
    filters:
    - RemoveRequestHeader=from2
    - AddRequestHeader=from,abc
    - name: Login
        args:
        checkLogin: true
  • 自定义过滤器代码@Slf4j
代码语言:txt复制
@Component
public class LoginGatewayFilterFactory extends AbstractGatewayFilterFactory<LoginCfg> {


public LoginGatewayFilterFactory() {

    super(LoginCfg.class);

}

@Override
public GatewayFilter apply(LoginCfg loginCfg) {

    return (exchange, chain) -> {
        log.info("if us check login:"  loginCfg.isCheckLogin());
        ServerHttpRequest request = exchange.getRequest().mutate()
                .build();
        if (loginCfg.isCheckLogin()) {
            log.info("do login checking.......");
        }
        return chain.filter(exchange.mutate().request(request).build());
    };
}
}

全局过滤器

image.pngimage.png

自定义全局过滤器

代码语言:txt复制
package cn.beckbi.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import reactor.core.publisher.Mono;

/**
 * @program: spring-cloud
 * @description:
 * @author: bikang
 * @create: 2022-08-21 20:21
 */
@Slf4j
@Configuration
public class GlobalFilterConfig {


    @Bean
    @Order(-2)
    public GlobalFilter filter1() {
        return (exchange, chain) -> {
            log.info("filter1 pre");
            return chain.filter(
                    exchange
            ).then(
                Mono.fromRunnable(()->{
                    log.info("filter1 post");
                })
            );
        };
    }

    @Bean
    @Order(0)
    public GlobalFilter filter2() {
        return (exchange, chain) -> {
            log.info("filter2 pre");
            return chain.filter(
                    exchange
            ).then(
                    Mono.fromRunnable(()->{
                        log.info("filter2 post");
                    })
            );
        };
    }

    @Bean
    @Order(2)
    public GlobalFilter filter3() {
        return (exchange, chain) -> {
            log.info("filter3 pre");
            return chain.filter(
                    exchange
            ).then(
                    Mono.fromRunnable(()->{
                        log.info("filter3 post");
                    })
            );
        };
    }
}

自定义拦截器

代码语言:txt复制
package cn.beckbi.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.loadbalancer.ResponseData;
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.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Map;
import java.util.Objects;

/**
 * @program: spring-cloud
 * @description:
 * @author: bikang
 * @create: 2022-08-21 20:29
 */
@Slf4j
@Component
public class UserFilter implements GlobalFilter, Ordered {

    @Builder
    @Data
    static class Resp {
        private int code;
        private String msg;
    }

    private static final String BAD_CID = "123";

    private ObjectMapper mapper = new ObjectMapper();

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

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();

        boolean matchFilter = false;

        if (Objects.nonNull(queryParams) && Objects.nonNull(queryParams.get("cid"))) {
            String cid = queryParams.get("cid").get(0);
            if (Objects.nonNull(cid) && BAD_CID.equals(cid)) {
                matchFilter = true;
            }
        }






        if (matchFilter) {
            ServerHttpResponse serverHttpResponse = exchange.getResponse();
            Resp resp = Resp.builder()
                    .code(401)
                    .msg("非法请求")
                    .build();
            DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(
                    this.getJsonBytes(resp)
            );
            serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
            serverHttpResponse.getHeaders().add("Content-Type", "application/json; charset=utf-8");


            return serverHttpResponse.writeWith(Mono.just(dataBuffer));
        }

        return chain.filter(exchange);
    }

    private byte[] getJsonBytes(Object o) {
        try {
            return mapper.writeValueAsBytes(o);
        }catch (JsonProcessingException e) {
            log.error("json error", e);
        }
        return "".getBytes();
    }


}

http://127.0.0.1:10011/info/1231212312?cid=123

限流器代码:基于redis做限流

代码语言:txt复制
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
代码语言:txt复制
package cn.beckbi.limiter;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * @program: spring-cloud
 * @description:
 * @author: bikang
 * @create: 2022-08-21 21:02
 */
@Configuration
public class LimiterConfig {

    @Bean("ipKeyResolver")
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(
                Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName()
        );
    }

    @Bean("cidKeyResolver")
    public KeyResolver cidKeyResolver() {
        return exchange -> Mono.just(
                Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("cid"))
        );
    }

    @Primary
    @Bean("apiKeyResolver")
    public KeyResolver apiKeyResolver() {
        return exchange -> {
            Route route = (Route) exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            return Mono.just(
                    route.getId() "#" exchange.getRequest().getPath().value()
            );
        };
    }
}

限流器配置

代码语言:txt复制
- name: RequestRateLimiter
    args:
    redis-rate-limiter.replenishRate: 1
    redis-rate-limiter.burstCapacity: 2
    key-resolver: "#{@apiKeyResolver}"

全局路由处理器

代码语言:txt复制
package cn.beckbi.errorhandler;



import cn.beckbi.util.JsonUtil;
import lombok.Builder;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
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 org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.util.annotation.Nullable;


/**
 * @program: spring-cloud
 * @description:
 * @author: bikang
 * @create: 2022-08-21 21:36
 */
@Component
@Slf4j
@Order(-1)
public class JsonHandler implements ErrorWebExceptionHandler {

    @Builder
    @Data
    static class Msg {
        int code;
        String msg;
    }
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {

        ServerHttpResponse response = exchange.getResponse();

        if (response.isCommitted()) {
            return Mono.error(ex);
        }

        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);


        ServerHttpRequest request = exchange.getRequest();
        String rawQuery = request.getURI().getRawQuery();
        String query = StringUtils.hasText(rawQuery) ? "?"   rawQuery : "";
        String path = request.getPath()   query ;
        String message ;
        HttpStatus status = null;
        if (ex instanceof ResponseStatusException) {
            status = ((ResponseStatusException) ex).getStatus();
        }

        if (status == null){
            status = HttpStatus.INTERNAL_SERVER_ERROR;
        }

        // 通过状态码自定义异常信息
        if (status.value() >= 400 && status.value() < 500){
            message = "路由服务不可达或禁止访问!";
        }else {
            message = "路由服务异常!";
        }
        message  = " path:"   path;

        Msg msg = Msg.builder().code(status.value())
                .msg(message)
                .build();


        return response
                .writeWith(Mono.fromSupplier(() -> {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    return bufferFactory.wrap(JsonUtil.getJsonBytes(msg));
                }));
    }

}

代码路径: https://github.com/beckbikang/spring-cloud/tree/main/kgateway

0 人点赞