spring gateway获取到post请求里面的参数并修改全局过滤器(filter)

package com.springboot.cloud.gateway.filter;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springboot.cloud.auth.client.service.IAuthService;
import com.springboot.cloud.common.core.util.EncryptionUtil;
import com.springboot.cloud.gateway.service.IPermissionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.DefaultServerRequest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

/**
 * 请求url权限校验
 */
@Configuration
@ComponentScan(basePackages = "com.springboot.cloud.auth.client")
@Slf4j
public class AccessGatewayFilter implements GlobalFilter {

    private static final String X_CLIENT_TOKEN_USER = "x-client-token-user";
    private static final String X_CLIENT_TOKEN = "x-client-token";

    /**
     * 由authentication-client模块提供签权的feign客户端
     */
    @Resource
    private IAuthService authService;

    @Resource
    private IPermissionService permissionService;

    /**
     * 1.首先网关检查token是否有效,无效直接返回401,不调用签权服务
     * 2.调用签权服务器看是否对该请求有权限,有权限进入下一个filter,没有权限返回401
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        
        ServerHttpRequest request = exchange.getRequest();
        String authentication = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        String method = request.getMethodValue();
        String url = request.getPath().value();
        ServerRequest serverRequest = new DefaultServerRequest(exchange);
        EncryptionUtil encryptionUtil = new EncryptionUtil();
      
        if ("POST".equals(method) ) {
            Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
                    .flatMap(body -> {
                       
                       //TODO 获取body内容去干你想做的事
                            return Mono.just(body );
                     

                    });

            BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
            HttpHeaders headers = new HttpHeaders();
            headers.putAll(exchange.getRequest().getHeaders());

            headers.remove(HttpHeaders.CONTENT_LENGTH);

            CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
            return bodyInserter.insert(outputMessage, new BodyInserterContext())
                    .then(Mono.defer(() -> {
                        ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
                                exchange.getRequest()) {
                            @Override
                            public HttpHeaders getHeaders() {
                                long contentLength = headers.getContentLength();
                                HttpHeaders httpHeaders = new HttpHeaders();
                                httpHeaders.putAll(super.getHeaders());
                                if (contentLength > 0) {
                                    httpHeaders.setContentLength(contentLength);
                                } else {
                                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                                }
                                return httpHeaders;
                            }

                            @Override
                            public Flux<DataBuffer> getBody() {
                                return outputMessage.getBody();
                            }
                        };
                        return chain.filter(exchange.mutate().request(decorator).build());
                    }));
        }

        log.debug("url:{},method:{},headers:{}", url, method, request.getHeaders());
        //不需要网关签权的url
        if (authService.ignoreAuthentication(url)) {
            return chain.filter(exchange);
        }

        //调用签权服务看用户是否有权限,若有权限进入下一个filter
        if (permissionService.permission(authentication, url, method)) {
            ServerHttpRequest.Builder builder = request.mutate();
            //TODO 转发的请求都加上服务间认证token
            builder.header(X_CLIENT_TOKEN, "添加服务间简单认证");
            //将jwt token中的用户信息传给服务
            builder.header(X_CLIENT_TOKEN_USER, getUserToken(authentication));
            return chain.filter(exchange.mutate().request(builder.build()).build());
        }
        return unauthorized(exchange);
    }


    /**
     * 提取jwt token中的数据,转为json
     *
     * @param authentication
     * @return
     */
    private String getUserToken(String authentication) {
        String token = "{}";
        try {
            token = new ObjectMapper().writeValueAsString(authService.getJwt(authentication).getBody());
            return token;
        } catch (JsonProcessingException e) {
            log.error("token json error:{}", e.getMessage());
        }
        return token;
    }

    /**
     * 网关拒绝,返回401
     *
     * @param
     */
    private Mono<Void> unauthorized(ServerWebExchange serverWebExchange) {

        serverWebExchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        DataBuffer buffer = serverWebExchange.getResponse()
                .bufferFactory().wrap(HttpStatus.UNAUTHORIZED.getReasonPhrase().getBytes());
        return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
    }
}


### 如何在 Spring Cloud Gateway获取 HTTP 请求对象及参数 #### 使用 `ServerWebExchange` 获取请求信息 在 Spring Cloud Gateway 的环境中,所有的请求处理都是基于 `ServerWebExchange` 对象完成的。此对象提供了访问当前HTTP请求和响应的能力[^1]。 ```java @Component public class CustomGlobalFilter implements GlobalFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); // 打印请求路径 System.out.println("Request Path: " + request.getURI().getPath()); // 处理逻辑... return chain.filter(exchange); } } ``` #### 解决 POST 请求体只能读取一次的问题 由于 Reactor Netty 和 WebFlux 的特性,在 Spring Cloud Gateway 中直接读取请求体会消耗掉输入流,使得后续处理器无法再读取该数据。为了克服这个问题,可以采用缓存请求体的方式: - 将原始请求转换成新的带有相同属性但可重复读取的内容的新请求; - 利用自定义过滤器来实现上述功能[^2]。 ```java import org.springframework.core.io.buffer.DataBuffer; import reactor.core.publisher.Flux; // 缓存请求创建新请求的方法 private static ServerHttpRequest copyRequestBody(ServerHttpRequest request) throws IOException { Flux<DataBuffer> cachedBody = request.getBody() .cache(); ServerHttpRequestDecorator decoratedRequest = new ServerHttpRequestDecorator(request) { @Override public Flux<DataBuffer> getBody() { return cachedBody; } }; return decoratedRequest; } ``` #### 日志记录中的应用实例 当希望在网关层面对所有进入系统的 API 调用做统一的日志管理时,可以通过全局过滤器截获每一个到来的请求从中提取必要的元数据以及可能存在的 JSON 形式的负载内容进行记录。 ```java @Slf4j @Component public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> { private final ObjectMapper objectMapper = new ObjectMapper(); public LoggingFilter() { super(Config.class); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { try { String bodyString = extractBodyAsString(exchange); log.info("Received Request:\nMethod:{}\nPath:{}\nHeaders:{}\nBody:{}", exchange.getRequest().getMethod(), exchange.getRequest().getURI().getPath(), exchange.getRequest().getHeaders(), bodyString); return chain.filter(exchange.mutate().request(copyRequestBody(exchange.getRequest())).build()); } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } }; } private String extractBodyAsString(ServerWebExchange exchange) throws Exception { StringBuilder builder = new StringBuilder(); exchange.getAttributeOrDefault("cached_request_body", "") .toString() .chars() .forEach(c -> builder.append((char)c)); if(builder.length() == 0){ return ""; } return builder.toString(); } public static class Config {} } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值