SpringCloud Gateway实现API接口加解密

目录
  • 接口范围
  • 启用禁用/版本
  • 加密算法
  • 报文格式
  • 网关实现细节代码
    • filter过滤器请求配置和请求方式分发
    • Get请求参数解密包装 ServerHttpRequestDecorator
    • post请求参数解密包装 ServerHttpRequestDecorator
    • GET/POST返回值加密处理CryptoServerHttpResponseDecorator
  • 完整CryptoFilter实现

接口范围

所有GET请求 白名单除外

body 体 是 application_json 和 application_json_utf8 的 POST请求 白名单除外

POST url传参也支持 白名单除外

启用禁用/版本

后端提供独立接口(或者现有接口)查询是否需要启用加密功能(如果后端启用了,前端请求被拦截修改为为启用,接口也无法访问回报解密错误),此接口明文传输

请求头增加一个加密版本字段,标识当前的加密算法版本:crypto-version: 1.0.0

加密算法

考虑到全局加密,使用AES加密方式性能更高

加密字符串:原始数据 > AES加密后的字节数组 > Base64编码处理

解密字符串:Base64密文 > AES密文 -> 原始字符串

AES加密细节

aesKey:32/16 位由后端同一生成

iv:aesKey

mode:CBC

padding:pkcs7

js例子

//加密
static encryptAES(data, key) {
  const dataBytes = CryptoJS.enc.Utf8.parse(data);
  const keyBytes = CryptoJS.enc.Utf8.parse(key);
  const encrypted = CryptoJS.AES.encrypt(dataBytes, keyBytes, {
    iv: keyBytes,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  });
  return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);
}

报文格式

GET

url:/app/xx/xx?xx=1

加密处理

秘钥:xxxxxxxxxxxxxxxx

加密文本:{"xx":1}

密文:xq4YR89LgUs4V5N5juKgW5hIsiOsCxBOwzX632S8NV4=

加密后的请求

/app/xx/xx?data=xq4YR89LgUs4V5N5juKgW5hIsiOsCxBOwzX632S8NV4=

POST

url:/app/xx/xx/xxx

json body:

{"xxx1":"111","xxx2":"huawei","xxx3":"789","xxx4":101,"xxx5":2}

加密处理

秘钥:xxxxxxxxxxxxxxxx

加密文本

{"xxx1":"111","xxx2":"huawei","xxx3":"789","xxx4":101,"xxx5":2}

密文:1oUTYvWfyaeTJ5/wJTVBqUv0Dz0IAUQTZtxSKY9WLZZl8pILP2Sozk5yOYg9I1WTvzgbbGRDGcWV1ASpYykyS1Fq5cT8s3aLXQ6NMo0AaMOC9L0aVpR863qWso5O8aG3

加密后的请求*

json body:

{

"data": "1oUTYvWfyaeTJ5/wJTVBqUv0Dz0IAUQTZtxSKY9WLZZl8pILP2Sozk5yOYg9I1WTvzgbbGRDGcWV1ASpYykyS1Fq5cT8s3aLXQ6NMo0AaMOjt4G9dK0WwhMGZofYuBKmdF27R8Qkr3VtZvjadtvBazJurITyE7hFcr43nlHSL5E="

}

POST url传参 和GET格式一致

网关实现细节代码

基于GlobalFilter 接口包装请求request和响应response,先列出关键代码,完整代码见文末

filter过滤器请求配置和请求方式分发

 @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!cryptoProperties.isEnabled()) {
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        //校验请求路径跳过加密
        String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange);
        String path = exchange.getRequest().getURI().getPath();
        if (isSkip(path) || isSkip(originalRequestUrl)) {
            return chain.filter(exchange);
        }

        HttpHeaders headers = request.getHeaders();
        MediaType contentType = headers.getContentType();

        //后期算法升级扩展,暂时只判断是否相等
        if (!cryptoProperties.getCryptoVersion().equals(headers.getFirst(cryptoProperties.getCryptoVersionHeader()))) {
            return Mono.error(new CryptoException("加密版本不支持"));
        }

        if (request.getMethod() == HttpMethod.GET) {
            return this.handleGetReq(exchange, chain);
        } else if (request.getMethod() == HttpMethod.POST &&
                (contentType == null ||
                        MediaType.APPLICATION_JSON.equals(contentType) ||
                        MediaType.APPLICATION_JSON_UTF8.equals(contentType))) {
            return this.handlePostReq(exchange, chain);
        } else {
            return chain.filter(exchange);
        }
    }

Get请求参数解密包装 ServerHttpRequestDecorator

//构造查询参数Map
        MultiValueMap<String, String> map = buildMultiValueMap(dataJson);
        //新的解密后的uri
        ServerHttpRequest newHttpRequest = this.buildNewServerHttpRequest(request, map);
        //新的解密后的uri request
        ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(newHttpRequest) {
            @Override
            public MultiValueMap<String, String> getQueryParams() {
                return map;
            }

        };

post请求参数解密包装 ServerHttpRequestDecorator

//构造一个请求包装
        final MultiValueMap<String, String> finalQueryParamMap = new LinkedMultiValueMap<>(queryParamMap);
        ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(request) {

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                return httpHeaders;
            }

            //处理post url传参解密
            @Override
            public MultiValueMap<String, String> getQueryParams() {
                if (queryParamsDecrypt) {
                    return finalQueryParamMap;
                }
                return super.getQueryParams();
            }

            @Override
            public Flux<DataBuffer> getBody() {
                //注意: 这里需要buffer一下,拿到完整报文后再map解密
                return super.getBody().buffer().map(buffer -> {
                    DataBuffer joinDataBuffer = dataBufferFactory.join(buffer);
                    byte[] content = new byte[joinDataBuffer.readableByteCount()];
                    joinDataBuffer.read(content);
                    DataBufferUtils.release(joinDataBuffer);
                    String decryptData = new String(content, StandardCharsets.UTF_8);
                    log.info("post decryptData: {}", decryptData);
                    if (!queryParamsDecrypt && StringUtils.isEmpty(decryptData)) {
                        throw new CryptoException("参数格式错误");
                    } else {
                        JSONObject dataJsonObj = JSON.parseObject(decryptData);
                        if (!queryParamsDecrypt && !dataJsonObj.containsKey(cryptoProperties.getParamName())) {
                            throw new CryptoException("参数格式错误");
                        }
                        byte[] bytes = AesUtil.decryptFormBase64(dataJsonObj.getString(cryptoProperties.getParamName()), cryptoProperties.getAesKey());
                        return dataBufferFactory.wrap(Objects.requireNonNull(bytes));
                    }
                });

GET/POST返回值加密处理CryptoServerHttpResponseDecorator

class CryptoServerHttpResponseDecorator extends ServerHttpResponseDecorator {
        final DataBufferFactory bufferFactory;
        boolean isPass = false;
        public CryptoServerHttpResponseDecorator(ServerHttpResponse delegate) {
            super(delegate);
            bufferFactory = delegate.bufferFactory();
        }

        @Override
        public HttpHeaders getHeaders() {
            HttpHeaders headers = super.getHeaders();
            //同一个请求此处有可能调用多次,先重置为false
            isPass = false;
            if (headers.getContentType() != null &&
                    !MediaType.APPLICATION_JSON.equals(headers.getContentType()) &&
                    !MediaType.APPLICATION_JSON_UTF8.equals(headers.getContentType())) {
                //相应体ContentType只处理json
                isPass = true;
            } else if (!headers.containsKey(cryptoProperties.getCryptoVersionHeader())) {
                //添加version响应头
                headers.add(cryptoProperties.getCryptoVersionHeader(), cryptoProperties.getCryptoVersion());
            }
            return headers;
        }

        //调用 writeWith 和 writeAndFlushWith 判断: NettyWriteResponseFilter

        // application/json;charset=UTF-8 走这里
        @Override
        public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
            if (body instanceof Flux && !isPass) {
                Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
                    DataBuffer joinDataBuffer = bufferFactory.join(dataBuffer);
                    byte[] content = new byte[joinDataBuffer.readableByteCount()];
                    joinDataBuffer.read(content);
                    DataBufferUtils.release(joinDataBuffer);
                    Map<String, String> data = new HashMap<>(1);
                    data.put(cryptoProperties.getParamName(), AesUtil.encryptToBase64(content, cryptoProperties.getAesKey()));
                    return bufferFactory.wrap(JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8));
                }));
            }
            return super.writeWith(body);
        }

        // StreamingMediaType类型:application/stream 和 application/stream+json 走这里
        @Override
        public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
            return super.writeAndFlushWith(body);
        }
    }

完整CryptoFilter实现

package org.xx.xx.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBufAllocator;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.xx.xx.gateway.props.CryptoProperties;
import org.xx.xx.gateway.provider.RequestProvider;
import org.xx.xx.gateway.provider.ResponseProvider;
import org.xx.xx.gateway.util.AesUtil;
import org.xx.xx.gateway.util.StringPool;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
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.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * CryptoFilter
 *
 * @author lizheng
 * @version 1.0
 * @date 2022/3/11 上午10:57
 */
@Slf4j
@RequiredArgsConstructor
@Configuration
@ConditionalOnProperty(value = "gateway.crypto.enabled", havingValue = "true", matchIfMissing = true)
public class CryptoFilter implements GlobalFilter, Ordered {

    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    private final DataBufferFactory dataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);

    private final CryptoProperties cryptoProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!cryptoProperties.isEnabled()) {
            return chain.filter(exchange);
        }
        ServerHttpRequest request = exchange.getRequest();
        //校验请求路径跳过加密
        String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange);
        String path = exchange.getRequest().getURI().getPath();
        if (isSkip(path) || isSkip(originalRequestUrl)) {
            return chain.filter(exchange);
        }

        HttpHeaders headers = request.getHeaders();
        MediaType contentType = headers.getContentType();

        //后期算法升级扩展,暂时只判断是否相等
        if (!cryptoProperties.getCryptoVersion().equals(headers.getFirst(cryptoProperties.getCryptoVersionHeader()))) {
            return Mono.error(new CryptoException("加密版本不支持"));
        }

        if (request.getMethod() == HttpMethod.GET) {
            return this.handleGetReq(exchange, chain);
        } else if (request.getMethod() == HttpMethod.POST &&
                (contentType == null ||
                        MediaType.APPLICATION_JSON.equals(contentType) ||
                        MediaType.APPLICATION_JSON_UTF8.equals(contentType))) {
            return this.handlePostReq(exchange, chain);
        } else {
            return chain.filter(exchange);
        }
    }

    class CryptoServerHttpResponseDecorator extends ServerHttpResponseDecorator {
        final DataBufferFactory bufferFactory;
        boolean isPass = false;
        public CryptoServerHttpResponseDecorator(ServerHttpResponse delegate) {
            super(delegate);
            bufferFactory = delegate.bufferFactory();
        }

        @Override
        public HttpHeaders getHeaders() {
            HttpHeaders headers = super.getHeaders();
            //同一个请求此处有可能调用多次,先重置为false
            isPass = false;
            if (headers.getContentType() != null &&
                    !MediaType.APPLICATION_JSON.equals(headers.getContentType()) &&
                    !MediaType.APPLICATION_JSON_UTF8.equals(headers.getContentType())) {
                //相应体ContentType只处理json
                isPass = true;
            } else if (!headers.containsKey(cryptoProperties.getCryptoVersionHeader())) {
                //添加version响应头
                headers.add(cryptoProperties.getCryptoVersionHeader(), cryptoProperties.getCryptoVersion());
            }
            return headers;
        }

        //调用 writeWith 和 writeAndFlushWith 判断: NettyWriteResponseFilter

        // application/json;charset=UTF-8 走这里
        @Override
        public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
            if (body instanceof Flux && !isPass) {
                Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
                    DataBuffer joinDataBuffer = bufferFactory.join(dataBuffer);
                    byte[] content = new byte[joinDataBuffer.readableByteCount()];
                    joinDataBuffer.read(content);
                    DataBufferUtils.release(joinDataBuffer);
                    Map<String, String> data = new HashMap<>(1);
                    data.put(cryptoProperties.getParamName(), AesUtil.encryptToBase64(content, cryptoProperties.getAesKey()));
                    return bufferFactory.wrap(JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8));
                }));
            }
            return super.writeWith(body);
        }

        // StreamingMediaType类型:application/stream 和 application/stream+json 走这里
        @Override
        public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
            return super.writeAndFlushWith(body);
        }
    }

    @SneakyThrows
    private Mono<Void> handlePostReq(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String paramData = request.getQueryParams().getFirst(cryptoProperties.getParamName());
        MultiValueMap<String, String> queryParamMap = new LinkedMultiValueMap<>();
        final boolean queryParamsDecrypt = !StringUtils.isEmpty(paramData);
        if (queryParamsDecrypt) {
            String dataJson;
            try {
                //AES解密
                dataJson = AesUtil.decryptFormBase64ToString(paramData, cryptoProperties.getAesKey());
            } catch (Exception e) {
                log.error("请求参数解密异常: ", e);
                return cryptoError(exchange.getResponse(), "请求参数解密异常");
            }
            //构造查询参数Map
            queryParamMap = buildMultiValueMap(dataJson);
            //新的解密后的uri request
            request = this.buildNewServerHttpRequest(request, queryParamMap);
        }

        //构造一个请求包装
        final MultiValueMap<String, String> finalQueryParamMap = new LinkedMultiValueMap<>(queryParamMap);
        ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(request) {

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.putAll(super.getHeaders());
                httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                return httpHeaders;
            }

            @Override
            public MultiValueMap<String, String> getQueryParams() {
                if (queryParamsDecrypt) {
                    return finalQueryParamMap;
                }
                return super.getQueryParams();
            }

            @Override
            public Flux<DataBuffer> getBody() {
                //注意: 这里需要buffer,拿到完整报文后再map解密
                return super.getBody().buffer().map(buffer -> {
                    DataBuffer joinDataBuffer = dataBufferFactory.join(buffer);
                    byte[] content = new byte[joinDataBuffer.readableByteCount()];
                    joinDataBuffer.read(content);
                    DataBufferUtils.release(joinDataBuffer);
                    String decryptData = new String(content, StandardCharsets.UTF_8);
                    log.info("post decryptData: {}", decryptData);
                    if (!queryParamsDecrypt && StringUtils.isEmpty(decryptData)) {
                        throw new CryptoException("参数格式错误");
                    } else {
                        JSONObject dataJsonObj = JSON.parseObject(decryptData);
                        if (!queryParamsDecrypt && !dataJsonObj.containsKey(cryptoProperties.getParamName())) {
                            throw new CryptoException("参数格式错误");
                        }
                        byte[] bytes = AesUtil.decryptFormBase64(dataJsonObj.getString(cryptoProperties.getParamName()), cryptoProperties.getAesKey());
                        return dataBufferFactory.wrap(Objects.requireNonNull(bytes));
                    }
                });
            }
        };
        return chain.filter(exchange.mutate()
                .request(decorator)
                .response(new CryptoServerHttpResponseDecorator(exchange.getResponse()))
                .build());
    }

    @SneakyThrows
    private Mono<Void> handleGetReq(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (request.getQueryParams().isEmpty()) {
            // get无参数 不走参数解密
            return chain.filter(exchange.mutate()
                    .request(request)
                    .response(new CryptoServerHttpResponseDecorator(exchange.getResponse()))
                    .build());
        }
        String paramData = request.getQueryParams().getFirst(cryptoProperties.getParamName());

        if (StringUtils.isEmpty(paramData)) {
            //有参数但是密文字段不存在
            throw new CryptoException("参数格式错误");
        }

        String dataJson;
        try {
            //AES解密
            dataJson = AesUtil.decryptFormBase64ToString(paramData, cryptoProperties.getAesKey());
        } catch (Exception e) {
            log.error("请求参数解密异常: ", e);
            return cryptoError(exchange.getResponse(), "请求参数解密异常");
        }
        //构造查询参数Map
        MultiValueMap<String, String> map = buildMultiValueMap(dataJson);
        //新的解密后的uri
        ServerHttpRequest newHttpRequest = this.buildNewServerHttpRequest(request, map);
        //新的解密后的uri request
        ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(newHttpRequest) {
            @Override
            public MultiValueMap<String, String> getQueryParams() {
                return map;
            }

        };
        return chain.filter(exchange.mutate()
                .request(decorator)
                .response(new CryptoServerHttpResponseDecorator(exchange.getResponse()))
                .build());
    }

    private MultiValueMap<String, String> buildMultiValueMap(String dataJson) {
        JSONObject jsonObject = JSON.parseObject(dataJson);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>(jsonObject.size());
        for (String key : jsonObject.keySet()) {
            map.put(key, Lists.newArrayList(jsonObject.getString(key)));
        }
        return map;
    }

    private ServerHttpRequest buildNewServerHttpRequest(ServerHttpRequest request, MultiValueMap<String, String> params) throws URISyntaxException {
        StringBuilder queryBuilder = new StringBuilder();
        for (String key : params.keySet()) {
            queryBuilder.append(key);
            queryBuilder.append(StringPool.EQUALS);
            queryBuilder.append(params.getFirst(key));
            queryBuilder.append(StringPool.AMPERSAND);
        }
        queryBuilder.deleteCharAt(queryBuilder.length() - 1);

        //经过测试只覆盖 ServerHttpRequest的getQueryParams路由分发之后,无法携带过去新的参数,所以这里需要构造一个新的解密后的uri
        URI uri = request.getURI();
        URI newUri = new URI(uri.getScheme(),
                uri.getUserInfo(),
                uri.getHost(),
                uri.getPort(),
                uri.getPath(),
                queryBuilder.toString(),
                uri.getFragment());

        //构造一个新的ServerHttpRequest
        return request.mutate().uri(newUri).build();
    }

    private boolean isSkip(String path) {
        for (String pattern : cryptoProperties.getSkipPathPattern()) {
            if (antPathMatcher.match(pattern, path)) {
                return true;
            }
        }
       return false;
    }

    private Mono<Void> cryptoError(ServerHttpResponse resp, String msg) {
        resp.setStatusCode(HttpStatus.UNAUTHORIZED);
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        String result = JSON.toJSONString(ResponseProvider.unAuth(msg));
        DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Flux.just(buffer));
    }

    @Override
    public int getOrder() {
        return -200;
    }
}

以上就是SpringCloud Gateway实现API接口加解密的详细内容,更多关于SpringCloud Gateway接口加解密的资料请关注我们其它相关文章!

(0)

相关推荐

  • SpringCloud Gateway读取Request Body方式

    目录 Gateway读取RequestBody 分析ReadBodyPredicateFactory 配置ReadBodyPredicateFactory 编写自定义GatewayFilterFactory 完整的yml配置 Gateway自定义filter获取body的数据为空 首先创建一个全局过滤器把body中的数据缓存起来 在自定义的过滤器中尝试获取body中的数据 解析body的工具类 Gateway读取Request Body 我们使用SpringCloud Gateway做微服务网关

  • SpringCloud Gateway详细分析实现负载均衡与熔断和限流

    目录 环境准备 1.pom依赖 2.yaml配置 3.路由转发和负载均衡测试 user服务暴露接口 返回结果输出 4.gateway熔断实现 4.1 熔断代码 4.2 测试 5.gateway限流 5.1 需要集成redis 5.2 yaml配置 5.3 注入到spring容器 5.4 测试 环境准备 注册中心Nacos,也可以其他 springboot 2.6.8 spring-cloud-dependencies 2021.0.3 1.pom依赖 parent包 <parent> <

  • springcloud gateway网关服务启动报错的解决

    目录 gateway网关服务启动报错 集成gateway 报错 原因分析 gateway网关运行时报错问题(版本问题) 父级中的版本问题 原因:父项目中的jdk版本问题 解决方法 gateway网关服务启动报错 集成gateway springcloud网关集成gateway服务 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter

  • springcloud整合gateway实现网关的示例代码

    目录 1.项目目录: 2.代码实现: 3.实现效果: 1.项目目录: 创建项目gateway作为父类 2.代码实现: 父类依赖 ​ <parent>         <groupId>org.springframework.boot</groupId>         <artifactId>spring-boot-starter-parent</artifactId>         <version>2.6.2</versi

  • SpringCloud Gateway之请求应答日志打印方式

    目录 Gateway请求应答日志打印 第一步 第二步 Gateway全局请求日志打印 把请求体的数据存入exchange 编写全局日志拦截器代码 在代码中配置全局拦截器 Gateway请求应答日志打印 请求应答日志时在日常开发调试问题的重要手段之一,那么如何基于Spring Cloud Gateway做呢,请看我上代码. 第一步 创建RecorderServerHttpRequestDecorator,缓存请求参数,解决body只能读一次问题. public class RecorderServ

  • springCloud gateWay 统一鉴权的实现代码

    目录 一,统一鉴权 1.1鉴权逻辑 1.2代码实现 一,统一鉴权 内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己 编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验. 1.1 鉴权逻辑 开发中的鉴权逻辑: 当客户端第一次请求服务时,服务端对用户进行信息认证(登录) 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证 以后每次请求,客户端都携带认证的token 服务端对token进行解密,判断是否有效

  • springcloud整合gateway实现网关全局过滤器功能

    目录 1.代码实现: 2.实现效果: springcloud整合gateway实现网关全局过滤器功能,在​ ​springcloud整合gateway实现网关 ​​基础功能上进行修改. 1.代码实现: 添加自定义全局过滤器 /**  * 自定义一个全局过滤器  * 实现 globalfilter , ordered接口  */ @Component public class LoginFilter implements GlobalFilter, Ordered {     /**      *

  • SpringCloud GateWay网关示例代码详解

    目录 一.网关基本概念 1.API网关介绍 2.Spring Cloud Gateway 3.Spring Cloud Gateway核心概念 一.网关基本概念 1.API网关介绍 API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:(1)客户端会多次请求不同的微服务,增加了客户端的复杂性.(2)存在跨域请求,在一定场景下处理相对复杂.(3)认证复杂,每个服务都

  • 基于SpringCloudGateway实现微服务网关的方式

    目录 (一)什么是微服务网关 (二)Spring Cloud Gateway网关 2.1 核心概念: 2.2 搭建环境: (三) 路由配置详解 3.1 自定义断言配置 3.2 断言不匹配404页面自定义 (四)Spring Cloud Gateway过滤器 (五) 网关限流 5.1 常见的一些限流算法: 5.2 集成Sentinel进行限流 5.3 网关实现跨域 (六)总结 (一)什么是微服务网关 后端写完所有的微服务之后,最终是要交给前端去调用.我们都知道每个微服务都有各自的端口号,如果前端直

  • SpringCloud Gateway实现API接口加解密

    目录 接口范围 启用禁用/版本 加密算法 报文格式 网关实现细节代码 filter过滤器请求配置和请求方式分发 Get请求参数解密包装 ServerHttpRequestDecorator post请求参数解密包装 ServerHttpRequestDecorator GET/POST返回值加密处理CryptoServerHttpResponseDecorator 完整CryptoFilter实现 接口范围 所有GET请求 白名单除外 body 体 是 application_json 和 ap

  • AES加解密在php接口请求过程中的应用示例

    在php请求接口的时候,我们经常需要考虑的一个问题就是数据的安全性,因为数据传输过程中很有可能会被用fillder这样的抓包工具进行截获.一种比较好的解决方案就是在客户端请求发起之前先对要请求的数据进行加密,服务端api接收到请求数据后再对数据进行解密处理,返回结果给客户端的时候也对要返回的数据进行加密,客户端接收到返回数据的时候再解密.因此整个api请求过程中数据的安全性有了一定程度的提高. 今天结合一个简单的demo给大家分享一下AES加解密技术在php接口请求中的应用. 首先,准备一个AE

  • SpringCloud Gateway加载断言predicates与过滤器filters的源码分析

    我们今天的主角是Gateway网关,一听名字就知道它基本的任务就是去分发路由.根据不同的指定名称去请求各个服务,下面是Gateway官方的解释: https://spring.io/projects/spring-cloud-gateway,其他的博主就不多说了,大家多去官网看看,只有官方的才是最正确的,回归主题,我们的过滤器与断言如何加载进来的,并且是如何进行对请求进行过滤的. 大家如果对SpringBoot自动加载的熟悉的话,一定知道要看一个代码的源码,要找到META-INF下的spring

  • SpringBoot实现接口数据的加解密功能

    一.加密方案介绍 对接口的加密解密操作主要有下面两种方式: 自定义消息转换器 优势:仅需实现接口,配置简单. 劣势:仅能对同一类型的MediaType进行加解密操作,不灵活. 使用spring提供的接口RequestBodyAdvice和ResponseBodyAdvice 优势:可以按照请求的Referrer.Header或url进行判断,按照特定需要进行加密解密. 比如在一个项目升级的时候,新开发功能的接口需要加解密,老功能模块走之前的逻辑不加密,这时候就只能选择上面的第二种方式了,下面主要

  • 基于NodeJS开发钉钉回调接口实现AES-CBC加解密

    钉钉小程序后台接收钉钉开放平台的回调比较重要,比如通讯录变动的回调,审批流程的回调都是在业务上十分需要的.回调接口时打通钉钉平台和内部系统的重要渠道. 但是给回调的接口增加了一些障碍,它需要支持回调的服务器的接口支持AES-CBC加解密.不然无法成功注册或解析内容. 钉钉官方文档中给出了JAVA,PHP,C#的后台SDK和demo,但是却没有Node服务器的代码支持,这让占有率很高的node服务器非常尴尬,难道node就不能作为钉钉平台的回调服务器么 好在钉钉已经开放了其加密算法,可以通过加密流

  • postman数据加解密实现APP登入接口模拟请求

    目录 主要使用到的Postman功能 数据加解密 各种参数设置 真正发送的数据: 请求处理脚本[Pro-request Script] 响应处理脚本[Tests] 结果的样子 主要使用到的Postman功能 环境变量:只要新建就好了,操作都是在代码中处理的. 日志查看:菜单位置:View → show postman console ,显示这个窗口视图就可以了 请求时执行的脚本:Pre-request Script 标签页,使用语言javascript, 通常作为加密. 接受返回时执行的脚本:T

  • SpringBoot接口数据加解密实战记录

    这日,刚撸完2行代码,正准备掏出手机摸鱼放松放松,只见老大朝我走过来,并露出一个”善意“的微笑,兴伟呀,xx项目有于安全问题,需要对接口整体进行加密处理,你这方面比较有经验,就给你安排上了哈,看这周内提测行不...,额,摸摸头上飘摇着而稀疏的长发,感觉我爱了. 和产品.前端同学对外需求后,梳理了相关技术方案, 主要的需求点如下: 尽量少改动,不影响之前的业务逻辑: 考虑到时间紧迫性,可采用对称性加密方式,服务需要对接安卓.IOS.H5三端,另外考虑到H5端存储密钥安全性相对来说会低一些,故分针对

  • SpringCloud Gateway 利用 Mysql 实现动态路由的方法

    需求描述 标准网关动态路由功能是重要的一环,将路由.断言以及过滤器信息,持久化到 Mysql 中,通过配置后台页面实现路由.断言.以及过滤器等配置的增删改查. Spring Cloud Gateway 路由及黑白名单实现背景 Spring Cloud 路由API Spring Cloud Gateway 通过定义 RouteDefinitionRepository 来实现动态路由. //保存路由缓存 public interface RouteDefinitionWriter { Mono<Vo

  • 详解SpringCloud Gateway 2020.0.2最新版

    简述 官网:https://spring.io/projects/spring-cloud-gateway GitHub地址:https://github.com/spring-cloud/spring-cloud-gateway 本文编写自2021年4月7日,当前SpringCloud最新版本为2020.0.2版本 本文使用版本为 SpringCloud 版本2020.0.2 spring-cloud-starter-gateway版本3.0.2 spring-boot-starter版本2.

随机推荐