SpringCloud gateway 统一请求拦截

在SpringCloud微服务框架下,可以通过网关gateway来进行统一的接口请求拦截,这里我主要用来做接口数据的加解密传输,这里使用了RSA非对称加密算法。(后面会附上完整代码)

首先先定义一个FilterConfig,实现GlobalFilter和Ordered两个接口

主要是实现filter拦截方法

 

 

rsaFilter函数的实现:

 整个拦截过程做了两件事:1、解密get请求参数,也就是url中的参数,2、解密body体中的请求参数,也就是post请求参数,这里前后端约定好了使用json传输。
注:这里不会直接修改请求,而创建了一个新的请求然后分发下去,如果有多个filter也是一样。
最终效果:
如果是get请求,后端收到的格式统一为:https://xxx?param=xxxxxxx
如果是post请求,后端收到的请求体格式统一为:{param: "xxxxxxx"}

 GatewayFilterConfig:

import com.alibaba.fastjson.JSONObject;
import com.huaihai.common.utils.RSA.*;
import io.jsonwebtoken.Claims;
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.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
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.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.lang.reflect.Field;
import java.net.URI;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.stream.Collectors;


/**
 * @author yeguodong
 * @date 2022/3/28
 */
@Configuration
@Component
public class GatewayFilterConfig implements GlobalFilter, Ordered {

    private static final String LOGIN_PATH = "/User/login";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){

        // 验证token,如果是登录或者非后台不校验Token

//        String requestUrl = exchange.getRequest().getPath().value();
//        String userType = exchange.getRequest().getHeaders().getFirst(UserConstant.USER_TYPE);
//        AntPathMatcher pathMatcher = new AntPathMatcher();
//        if (!pathMatcher.match(LOGIN_PATH, requestUrl) && "admin".equalsIgnoreCase(userType)) {
//            String token = exchange.getRequest().getHeaders().getFirst(UserConstant.TOKEN);
//            Claims claim = TokenUtils.getClaim(token);
//            if (StringUtils.isBlank(token) || claim == null) {
//                return FilterUtils.invalidToken(exchange);
//            }
//        }


        return rsaFilter(exchange, chain);
        // 测试用,不拦截
//        return chain.filter(exchange);

    }

    /**
     * 拦截请求,rsa解密参数
     * @param exchange
     * @param chain
     * @return
     */
    private Mono rsaFilter(ServerWebExchange exchange, GatewayFilterChain chain) {

        MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
        if(!(MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)
                || MediaType.APPLICATION_JSON_UTF8.isCompatibleWith(mediaType))) {
            return chain.filter(exchange);
        }

        try {
            updateRequestParam(exchange);
        } catch (Exception e) {
            e.printStackTrace();
            return FilterUtils.invalidUrl(exchange);
        }

        ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());

        Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
                .flatMap(body -> {
                    JSONObject jsonObject = JSONObject.parseObject(body);
                    String param = "";
                    try {
                        param = RSAUtils.decrypt(jsonObject.getString("param"), RSAConstant.PRIVATE_KEY);
                    } catch (Exception e) {

                    }

                    return Mono.just(param);
                });

        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());
                }));
    }

    /**
     * 修改前端传的Get参数
     */
    private void updateRequestParam(ServerWebExchange exchange)
            throws NoSuchFieldException, IllegalAccessException, NoSuchPaddingException, NoSuchAlgorithmException,
            IllegalBlockSizeException, BadPaddingException, InvalidKeyException, InvalidKeySpecException {
        ServerHttpRequest request = exchange.getRequest();
        URI uri = request.getURI();
        String query = uri.getQuery();
        String[] split;
        if (StringUtils.isNotBlank(query)
                && query.contains("param")
                && (split = query.split("=")).length > 0) {
            String param = RSAUtils.decrypt(split[1], RSAConstant.PRIVATE_KEY);
            Field targetQuery = uri.getClass().getDeclaredField("query");
            targetQuery.setAccessible(true);
            targetQuery.set(uri, getParams(param));
        }
    }

    private String getParams(String query) {
        JSONObject jsonObject = JSONObject.parseObject(query);
        return jsonObject.entrySet()
                .stream()
                .map(e -> e.getKey() + "=" + e.getValue())
                .collect(Collectors.joining("&"));
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE;
    }
}

GatewayContext:

import org.springframework.util.MultiValueMap;

/**
 * @author yeguodong
 * @date 2022/4/6
 */
public class GatewayContext {

    public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext";

    /**
     * cache json body
     */
    private String cacheBody;
    /**
     * cache formdata
     */
    private MultiValueMap<String, String> formData;
    /**
     * cache reqeust path
     */
    private String path;


    public String getCacheBody() {
        return cacheBody;
    }

    public void setCacheBody(String cacheBody) {
        this.cacheBody = cacheBody;
    }

    public MultiValueMap<String, String> getFormData() {
        return formData;
    }

    public void setFormData(MultiValueMap<String, String> formData) {
        this.formData = formData;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }
}

RSAUtils:

import org.apache.commons.codec.binary.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yeguodong
 * @date 2022/3/31
 */
public class RSAUtils {

    public static final String PUBLIC_KEY = "public_key";

    public static final String PRIVATE_KEY = "private_key";

    public static String encrypt(String str, String publicKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
            InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        //base64编码的公钥
        byte[] decoded = Base64.decodeBase64(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        return Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
    }


    public static String decrypt(String str, String privateKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException,
            InvalidKeyException, BadPaddingException, IllegalBlockSizeException {

        //64位解码加密后的字符串
        byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8));
        //base64编码的私钥
        byte[] decoded = Base64.decodeBase64(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        return new String(cipher.doFinal(inputByte));
    }

    private static Map<String, String> generateRasKey() {
        Map<String, String> rs = new HashMap<>();
        try {
            // KeyPairGenerator类用于生成公钥和私钥对,基于RSA算法生成对象
            KeyPairGenerator keyPairGen = null;
            keyPairGen = KeyPairGenerator.getInstance("RSA");
            keyPairGen.initialize(1024, new SecureRandom());
            // 生成一个密钥对,保存在keyPair中
            KeyPair keyPair = keyPairGen.generateKeyPair();
            // 得到私钥 公钥
            RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
            RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
            String publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()));
            // 得到私钥字符串
            String privateKeyString = new String(Base64.encodeBase64((privateKey.getEncoded())));
            // 将公钥和私钥保存到Map
            rs.put(PUBLIC_KEY, publicKeyString);
            rs.put(PRIVATE_KEY, privateKeyString);
        } catch (Exception e) {

        }
        return rs;
    }

    public static void main(String[] args) {
        System.out.println(generateRasKey());
    }

}

FilterUtils:

import com.alibaba.fastjson.JSONObject;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

/**
 * @author yeguodong
 * @date 2022/4/1
 */
public class FilterUtils {

    private FilterUtils() {

    }

    public static Mono<Void> invalidToken(ServerWebExchange exchange) {
        JSONObject json = new JSONObject();
        json.put("code", HttpStatus.UNAUTHORIZED.value());
        json.put("msg", "无效的token");
        return buildReturnMono(json, exchange);
    }

    public static Mono<Void> invalidUrl(ServerWebExchange exchange){
        JSONObject json = new JSONObject();
        json.put("code", HttpStatus.BAD_REQUEST.value());
        json.put("msg", "无效的请求");
        return buildReturnMono(json, exchange);
    }


    public static Mono<Void> buildReturnMono(JSONObject json, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        byte[] bits = json.toJSONString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        //指定编码,否则在浏览器中会中文乱码
        response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
        return response.writeWith(Mono.just(buffer));
    }
}

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Cloud Gateway 是一个反向代理和路由器,它可以拦截进入的请求并将它们路由到不同的服务。Spring Cloud Gateway 可以通过以下步骤获取请求: 1. 在 Spring Cloud Gateway 的路由配置中,定义一个过滤器: ```java @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("path_route", r -> r.path("/foo/**") .filters(f -> f.filter(new RequestBodyFilter())) .uri("http://localhost:8080")) .build(); } ``` 这里定义了一个名为 `RequestBodyFilter` 的过滤器,用于获取请求。 2. 实现 `RequestBodyFilter` 过滤器: ```java public class RequestBodyFilter implements GatewayFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { return exchange.getRequest().getBody() .flatMap(body -> { // 处理请求 byte[] bytes = new byte[body.readableByteCount()]; body.read(bytes); String requestBody = new String(bytes); System.out.println(requestBody); // 重新设置请求 DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); exchange.getRequest().mutate().body(buffer); return chain.filter(exchange); }); } } ``` 这里的 `filter` 方法会获取请求,并进行处理。处理完成后,需要重新设置请求,以便后续的过滤器或路由器可以正确地处理请求。 3. 在请求中发送请求: ```bash curl -X POST http://localhost:8080/foo -d '{"name": "John"}' ``` 这里使用 `curl` 命令发送一个 POST 请求,并在请求中包含 JSON 数据。Spring Cloud Gateway拦截这个请求,并使用定义的过滤器获取请求。 以上就是 Spring Cloud Gateway 获取请求的方法。需要注意的是,获取请求可能会影响性能,因此应该谨慎使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值