gateway获取请求体和响应体

gateway获取请求体和响应体

GateWay 获取响应结果,处理大数据分段传输问题

GlobalCacheRequestFilter

package com.yymt.project.filter;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.yymt.project.config.SwaggerProvider;
import com.yymt.project.feign.AuthFeignClient;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
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.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @calssName AppCacheRequestBodyFilter
 * @Description 将 request 参数 中的内容 copy 一份,记录到 exchange 的一个自定义属性中
 */
@Slf4j
@Component
public class GlobalCacheRequestFilter implements GlobalFilter, Ordered {

    public final static String CACHED_REQUEST_PARAM_KEY = "CACHED_REQUEST_PARAM_KEY";
    public final static String SYS_LOG_ID = "sysLogId";
    public final static Snowflake snowflake = IdUtil.getSnowflake(1, 1);
    private int order;
    @Resource
    AuthFeignClient authFeignClient;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // String path = exchange.getRequest().getPath().toString();
        ServerHttpRequest request = exchange.getRequest();
        // String method = request.getMethodValue();
        HttpHeaders headers = request.getHeaders();
        String sysLogId = String.valueOf(snowflake.nextId());

        // 将 request body 中的内容 copy 一份,记录到 exchange 的一个自定义属性中
        Object cachedRequestBodyObject = exchange.getAttributeOrDefault(CACHED_REQUEST_PARAM_KEY,
                null);
        // 如果已经缓存过,或者白名单略过
        if (cachedRequestBodyObject != null) {
            return chain.filter(exchange);
        }

        // 如果没有缓存过,获取字节数组存入 exchange 的自定义属性中
        String contentType = exchange.getRequest().getHeaders().getFirst("Content-Type");
        if (StrUtil.isNotBlank(contentType) && contentType.contains(MediaType.APPLICATION_JSON_VALUE)) {
            return DataBufferUtils.join(exchange.getRequest().getBody()).map(dataBuffer -> {
                        byte[] bytes = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(bytes);
                        DataBufferUtils.release(dataBuffer);
                        return bytes;
                    }).defaultIfEmpty(new byte[0])
                    .doOnNext(bytes -> {
                        String param = new String(bytes, StandardCharsets.UTF_8);
                        logtrace(exchange, param);
                        exchange.getAttributes().put(CACHED_REQUEST_PARAM_KEY, param);
                        exchange.getAttributes().put(SYS_LOG_ID, sysLogId);
                        // }).then(chain.filter(exchange));
                    }).then(chain.filter(exchange.mutate().response(recordResponseLog(exchange)).build()));
        }

        Map m = request.getQueryParams();
        String param = Convert.toStr(m);
        logtrace(exchange, param);
        exchange.getAttributes().put(CACHED_REQUEST_PARAM_KEY, param);
        exchange.getAttributes().put(SYS_LOG_ID, sysLogId);

        return chain.filter(exchange.mutate().response(recordResponseLog(exchange)).build());
        // return chain.filter(exchange.mutate().request(recordRequestLog(exchange)).response(recordResponseLog(exchange)).build());
        // return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    public GlobalCacheRequestFilter() {
        this.order = -30;
    }

    public GlobalCacheRequestFilter(int order) {
        this.order = order;
    }

    /**
     * 日志信息
     *
     * @param exchange
     * @param param    请求参数
     */
    private void logtrace(ServerWebExchange exchange, String param) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String path = serverHttpRequest.getURI().getPath();
        String method = serverHttpRequest.getMethodValue();
        String headers = serverHttpRequest.getHeaders().entrySet()
                .stream()
                .map(entry -> "            " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]")
                .collect(Collectors.joining("\n"));
        log.debug("\n" + "------------------------------------------------>>\n" +
                        "HttpMethod : {}\n" +
                        "Uri        : {}\n" +
                        "Param      : {}\n" +
                        "Headers    : \n" +
                        "{}\n" +
                        "\"<<------------------------------------------------"
                , method, path, param, headers);
    }

    /**
     * 记录响应日志
     */
    private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = exchange.getRequest().getHeaders();
        MediaType mediaType = headers.getContentType();
        String url = exchange.getRequest().getURI().getPath();
        DataBufferFactory bufferFactory = response.bufferFactory();

        ServerHttpResponseDecorator decoratorResponse = new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    // 没有token的不会记录日志,所以不记录响应
                    if (StrUtil.isBlank(headers.getFirst("Authorization"))) {
                        return super.writeWith(body);
                    }
                    // 过滤上传附件请求
                    if ((mediaType != null && mediaType.equals(MediaType.MULTIPART_FORM_DATA))
                            || (mediaType != null && mediaType.equals(MediaType.APPLICATION_FORM_URLENCODED))) {
                        return super.writeWith(body);
                    }
                    if (url.contains(SwaggerProvider.API_URI)) {
                        return super.writeWith(body);
                    }

                    Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {// 解决返回体分段传输获取完整响应数据问题
                        StringBuilder bodyString = new StringBuilder();
                        dataBuffers.forEach(dataBuffer -> {
                            byte[] content = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(content);
                            DataBufferUtils.release(dataBuffer);
                            bodyString.append(new String(content, CharsetUtil.UTF_8));
                        });

                        // 拿到返回数据,可以进行做一些修改
                        String result = bodyString.toString();
                        byte[] uppedContent = new String(result.getBytes(), CharsetUtil.UTF_8).getBytes();
                        // 保存响应日志
                        sysLogResAdd(exchange, result);
                        response.getHeaders().setContentLength(uppedContent.length);
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                return super.writeWith(body);
            }
        };

        return decoratorResponse;
    }

    /**
     * 保存响应日志
     *
     * @param exchange
     * @param result
     */
    private void sysLogResAdd(ServerWebExchange exchange, String result) {
        // 根据结果处理自己的逻辑
        String sysLogId = exchange.getAttributeOrDefault(GlobalCacheRequestFilter.SYS_LOG_ID, "");
        log.info("===============sysLogId:{},responseResult:{}", sysLogId, result);
        Map<String, Object> sysLogResMap = new HashMap<>();
        sysLogResMap.put("id", sysLogId);
        sysLogResMap.put("result", result);
        authFeignClient.sysLogResAdd(sysLogResMap);
    }

}

AuthorityFilter

package com.yymt.project.filter;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yymt.project.dto.HasPermissionReqDTO;
import com.yymt.project.entity.User;
import com.yymt.project.entity.common.CodeEnum;
import com.yymt.project.entity.common.Result;
import com.yymt.project.feign.AuthFeignClient;
import com.yymt.project.filter.util.ModifiedRequestDecorator;
import com.yymt.project.filter.util.RecorderServerHttpRequestDecorator;
import com.yymt.project.filter.util.RewriteConfig;
import com.yymt.project.filter.util.SensitiveWordUtils;
import com.yymt.project.util.IpUtil;
import com.yymt.project.util.JwtUtil;
import com.yymt.project.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
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.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 自定义网关权限全局过滤器
 *
 * @author: hubin
 * @date: 2020/12/17 16:21
 */
@Slf4j
@Component
public class AuthorityFilter implements GlobalFilter, Ordered {

    /**
     * 注入权限远程用
     */
    @Resource
    AuthFeignClient authFeignClient;

    /**
     * 注入权限远程用
     */
    @Resource
    SensitiveWordUtils sensitiveWordUtils;

    /**
     * 访问白名单
     */
    @Value("${access.whitelist}")
    private String whitelist;

    /**
     * 敏感词过滤url
     */
    @Value("${access.sensitiveWordFilterUrl:}")
    private String sensitiveWordFilterUrl;

    private static final String OPTIONS = "OPTIONS";

    /**
     * 注入redis工具类
     */
    @Resource
    RedisUtils redisUtils;

    /**
     * ip名单列表
     */
    public static String REDIS_IP_LIST_KEY = "ip_list_key:";
    /**
     * ip名单类型
     */
    public static String REDIS_IP_LIST_TYPE_KEY = "ip_list_type_key:";

    /**
     * 重写过滤器
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 放行不需要鉴权请求
        String path = exchange.getRequest().getPath().toString();

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        HttpHeaders headers = exchange.getRequest().getHeaders();

        // 获取token
        String token = headers.getFirst("Authorization");
        String requestHeaderVersion = headers.getFirst("version");
        String contentType = headers.getFirst("Content-Type");

        Integer version = null;
        // 请求体对象
        JSONObject bodyObject = null;
        if ((request.getMethod() == HttpMethod.POST || request.getMethod() == HttpMethod.PUT) && "application/json".equals(contentType)) {
            AtomicReference<String> requestBody = new AtomicReference<>("");
            RecorderServerHttpRequestDecorator requestDecorator = new RecorderServerHttpRequestDecorator(request);
            Flux<DataBuffer> body = requestDecorator.getBody();
            body.subscribe(buffer -> {
                CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                requestBody.set(charBuffer.toString());
            });
            //获取body参数
            String bodyString = requestBody.get();
            if (StringUtils.isNotBlank(bodyString) && bodyString.startsWith("{")) {
                bodyObject = JSONObject.parseObject(bodyString);
            }
        }

        if (StringUtils.isNotBlank(requestHeaderVersion)) {
            // 如果请求头设置了版本号,优先设置请求头版本号
            version = Integer.valueOf(requestHeaderVersion);
        } else if (bodyObject != null) {
            // 请求头未设置版本号,从请求体获取版本号参数
            if (bodyObject.getInteger("version") != null) {
                version = bodyObject.getInteger("version");
            }
        }
        if (StringUtils.isNotBlank(token)) {
            // 请求头和请求体都没有版本号参数,从token获取版本号
            version = JwtUtil.getVersion(token);
        }
        if (version == null) {
            // 无版本参数数据,设置为默认1
            version = 1;
        }

        // 获取ip
        String ip = IpUtil.getIpAddress(exchange.getRequest());
        // 如果是web端使用,则需要处理ip禁用逻辑
        boolean needCheckIp = false;
        //  loginMode 登录方式:1表示Web端登录,2表示移动端登录,3表示3D客户端登录,4客户端登录
        if (StringUtils.isNotBlank(token)) {
            if (JwtUtil.getUserId(token).longValue() != 1 && JwtUtil.getLoginMode(token) == 1) {
                // 非admin账号又是web端接口需要校验ip
                needCheckIp = true;
            }
        } else if (bodyObject != null) {
            // 判断body数据是否携带loginMode参数
            if (bodyObject.getInteger("loginMode") != null) {
                if (bodyObject.getInteger("loginMode") == 1) {
                    needCheckIp = true;
                }
            }
        }
        if (needCheckIp) {
            // 判断黑白名单
            String ipListType = redisUtils.get(REDIS_IP_LIST_TYPE_KEY + version);
            String ipList = redisUtils.get(REDIS_IP_LIST_KEY + version);
            if (StringUtils.isNotBlank(ipListType) && StringUtils.isNotBlank(ipList)) {
                String[] ipRuleArr = ipList.split(",");
                if ("1".equals(ipListType)) {
                    // 判断黑名单
                    for (String ipRule : ipRuleArr) {
                        if (IpUtil.checkIpByRule(ip, ipRule)) {
                            // 属于黑名单ip
                            response.setStatusCode(HttpStatus.LOCKED);
                            Result result = new Result().setCode(HttpStatus.LOCKED.value()).setMsg("ip已被禁用");
                            DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
                            return response.writeWith(Mono.just(buffer));
                        }
                    }
                }
                if ("0".equals(ipListType)) {
                    // 是否属于白名单
                    boolean isAllow = false;
                    for (String ipRule : ipRuleArr) {
                        if (IpUtil.checkIpByRule(ip, ipRule)) {
                            // 属于白名单ip
                            isAllow = true;
                            break;
                        }
                    }
                    if (!isAllow) {
                        // 不属于白名单
                        response.setStatusCode(HttpStatus.LOCKED);
                        Result result = new Result().setCode(HttpStatus.LOCKED.value()).setMsg("ip已被禁用");
                        DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
                        return response.writeWith(Mono.just(buffer));
                    }
                }
            }
        }

        // 获取参数
        String params = "none";
        String sysLogId = "";
        try {
            // 从 exchange 的自定义属性中取出缓存到的 body
            params = exchange.getAttributeOrDefault(GlobalCacheRequestFilter.CACHED_REQUEST_PARAM_KEY, "none");
            sysLogId = exchange.getAttributeOrDefault(GlobalCacheRequestFilter.SYS_LOG_ID, "");
            if (params != null) {
                // 取完数据清空缓存
                exchange.getAttributes().remove(params);
            }
        } catch (Exception e) {
            log.error("get params error!");
        }
        // log.info("====>request path:{} ,\n params:{}", path, params);

        String[] whites = whitelist.split(",");
        boolean flag = false;
        for (String white : whites) {
            if (path.indexOf(white) != -1) {
                flag = true;
                break;
            }
        }
        List<String> filterUrlList = Arrays.asList(sensitiveWordFilterUrl.split(","));
        if (flag) {
            // 判断是否敏感词过滤url
            if (filterUrlList.contains(path)) {
                if (request.getMethod() == HttpMethod.POST || request.getMethod() == HttpMethod.PUT) {
                    return new ModifiedRequestDecorator(exchange, new RewriteConfig()
                            .setRewriteFunction(String.class, String.class, (ex, requestData)
                                    -> Mono.just(sensitiveWordUtils.replaceWord(requestData))
                            )).filter(exchange, chain);
                } else {
                    return chain.filter(exchange);
                }
            } else {
                return chain.filter(exchange);
            }
        }

        // 响应类型
        // response.getHeaders().add("Content-Type", "application/json; charset=utf-8");
        if (StringUtils.isNotBlank(token)) {
            // 远程调用鉴权服务
            Result successResult = null;
            try {
                HasPermissionReqDTO reqDTO = new HasPermissionReqDTO().setToken(token).setUrl(path)
                        .setServiceName(path.split("/")[1]).setIp(ip).setParams(params)
                        .setSysLogId(Convert.toLong(sysLogId));
                successResult = authFeignClient.hasPermission(reqDTO);
            } catch (Exception e) {
                log.error("鉴权服务异常!", e);
                // 其他异常
                response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                // 响应内容
                Result result = new Result().setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()).setMsg("鉴权服务异常");
                DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
                return response.writeWith(Mono.just(buffer));
            }
            // 鉴权不通过
            if (ObjectUtils.isEmpty(successResult) || CodeEnum.TOKEN_EXPIRE.getCode().equals(successResult.getCode())) {
                // 响应状态码,HTTP 401 错误代表用户没有访问权限
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                // 响应内容
                Result result = new Result().setCode(HttpStatus.UNAUTHORIZED.value()).setMsg("登录已过期,请重新登录");
                DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
                return response.writeWith(Mono.just(buffer));
            } else if (CodeEnum.SUCCESS.getCode().equals(successResult.getCode())) {
                User data = JSON.parseObject(JSON.toJSONString(successResult.getData()), User.class);
                // 向headers中放文件,记得build
                ServerHttpRequest host = exchange.getRequest().mutate().header("x-user-id", data.getUserId().toString())
                        .header("x-user-mobile", data.getMobile()).header("Authorization", token)
                        .header("admin", data.getAdmin().toString())
                        .header("user-type", data.getUserType() == null ? "" : data.getUserType().toString())
                        .header("system-version", String.valueOf(version))
                        .header("communityId", data.getCommunityId() == null ? "" : data.getCommunityId().toString())
                        // .header("secondCall","true")
                        .header("communityIds", StringUtils.join(data.getCommunityIds().toArray(), ",")).build();
                // 将现在的request 变成 change对象
                ServerWebExchange build = exchange.mutate().request(host).build();
                // 判断是否敏感词过滤url
//                AuthFeignClient feignClient = SpringUtil.getBean(AuthFeignClient.class);

                if (filterUrlList.contains(path)) {
                    if (request.getMethod() == HttpMethod.POST || request.getMethod() == HttpMethod.PUT) {

                        return new ModifiedRequestDecorator(build, new RewriteConfig()
                                .setRewriteFunction(String.class, String.class, (ex, requestData)
                                        -> Mono.just(requestData == null ? "" : sensitiveWordUtils.replaceWord(requestData))
                                )).filter(build, chain);
                    } else {
                        return chain.filter(build);
                    }
                } else {
                    return chain.filter(build);
                }
            } else if (CodeEnum.FORBIDDEN.getCode().equals(successResult.getCode())) {
                // 响应状态码,HTTP 401 错误代表用户没有访问权限
                response.setStatusCode(HttpStatus.FORBIDDEN);
                // 响应内容
                Result result = new Result().setCode(HttpStatus.FORBIDDEN.value()).setMsg("无权限,请联系管理员!");
                DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
                return response.writeWith(Mono.just(buffer));
            } else if (CodeEnum.REMOTE_LOGIN.getCode().equals(successResult.getCode())) {
                // 响应状态码,HTTP 401 错误代表用户异地登录
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                // 响应内容
                Result result = new Result().setCode(HttpStatus.UNAUTHORIZED.value())
                        .setMsg(CodeEnum.REMOTE_LOGIN.getMsg());
                DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
                return response.writeWith(Mono.just(buffer));
            } else {
                // 其他异常
                response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                // 响应内容
                Result result = new Result().setCode(HttpStatus.INTERNAL_SERVER_ERROR.value())
                        .setMsg(CodeEnum.COMMON_ERROR.getMsg());
                DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
                return response.writeWith(Mono.just(buffer));
            }
        }
        // 响应状态码,HTTP 401 错误代表用户没有访问权限
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        // 响应内容
        Result result = new Result().setCode(HttpStatus.UNAUTHORIZED.value())
                .setMsg("登录已过期,请重新登录");
        DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
        return response.writeWith(Mono.just(buffer));
    }

    /**
     * 过滤器执行顺序,数值越小,优先级越高
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

你可能感兴趣的:(gateway,java,spring)