Gateway 接口参数加解密

方案

原理:

1. 使用AES随机生成Key,加解密参数;

2. 使用RSA加解密Key;

具体操作:

1. 前端使用随机Key加密参数,使用固定RSA秘钥加密Key,请求到后端;

2. 后端收到加密Key,以及加密参数,先使用RSA解密Key,再用解密Key解密参数;

3. 返回使用原Key加密返回参数,使用RSA加密Key;

即:

1. 前端请求公钥加密,后端收到私钥解密;

2. 后端返回私钥加密,前端收到公钥解密。

ReqFilter

package cn.nocov.hospital.gateway.filter;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.nocov.hospital.gateway.config.redis.RedisUtil;
import cn.nocov.hospital.gateway.util.IpUtil;
import cn.nocov.hospital.gateway.util.NacosCfgUtil;
import cn.nocov.hospital.gateway.util.RequestUriUtil;
import cn.nocov.hospital.gateway.util.RsaAesUtil;
import cn.nocov.hospital.gateway.util.TokenUtil;
import cn.nocov.hospital.gateway.util.TokenUtil.ProductPlatformEnum;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
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.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequest.Builder;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.RequestMethod;
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 org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author: Zek
 * @date: 2020/8/13 on 4:36 下午
 * @description: 请求过滤器,注意:@Order、Ordered虽然都是执行顺序,但是使用@Order注解会导致url少一位,不知道啥问题
 */
@Slf4j
@Configuration
public class ReqFilter implements GlobalFilter, Ordered {

  @Resource private NacosCfgUtil nacosCfgUtil;
  @Resource private RedisUtil redisUtil;
  @Resource private RequestUriUtil requestUriUtil;

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    MDC.clear();
    ServerHttpRequest request = exchange.getRequest();
    ServerHttpResponse response = exchange.getResponse();
    if (RequestMethod.OPTIONS.name().equals(request.getMethodValue())) {
      return chain.filter(exchange);
    }
    URI requestUri = request.getURI();
    String uri = requestUri.getPath();
    String url = requestUri.toString();
    HttpHeaders headers = request.getHeaders();
    ProductPlatformEnum appPlatform =
        ProductPlatformEnum.toEnum(headers.getFirst(Constant.HEADER_PLATFORM));
    MDC.put(Constant.HEADER_PLATFORM, appPlatform == null ? "" : appPlatform.name());
    String appVersion = headers.getFirst(Constant.HEADER_VERSION);
    MDC.put(Constant.HEADER_VERSION, appVersion);
    String requestId = headers.getFirst(Constant.HEADER_REQUEST_ID);
    MDC.put(Constant.HEADER_REQUEST_ID, requestId);
    HttpMethod method = request.getMethod();
    log.info(
        "{}--------------------》Send {} To {}\n{}",
        requestId,
        method,
        url,
        headers.toSingleValueMap());
    boolean needEncrypt = requestUriUtil.noTokenNeedEncrypt(uri);
    if (needEncrypt) {
      // 判断请求方式是否需要加密
      needEncrypt = needEncryptJudgeRequest(headers, method);
      if (needEncrypt && !TokenUtil.checkHeader(appPlatform, appVersion, requestId)) {
        return errorReturn(response, requestId, "512", "版本过低,请前往商店更新最新版本");
      }
    } else {
      if (CharSequenceUtil.isNotBlank(requestId)) {
        requestId = TokenUtil.getRequestId();
      }
    }
    if (CharSequenceUtil.isNotBlank(requestId)) {
      response.getHeaders().set(Constant.HEADER_REQUEST_ID, requestId);
    }
    String ip = IpUtil.getIp(request);
    Builder mutate =
        request
            .mutate()
            .header(Constant.HEADER_IP, ip)
            .header(Constant.HEADER_REQUEST_ID, requestId);
    // 需要校验token的接口
    String token = "";
    if (requestUriUtil.needToken(uri)) {
      if (appPlatform == null) {
        log.info("{}--------------------》登录校验失败:appPlatform == null,url:{}", requestId, url);
        return errorReturn(response, requestId, "302", "登录验证失败,请重新登录");
      }
      token = TokenUtil.getToken(request, appPlatform);
      if (CharSequenceUtil.isBlank(token)) {
        log.info("{}--------------------》登录校验失败:cookie token == null/'',url:{}", requestId, url);
        return errorReturn(response, requestId, "302", "登录验证失败,请重新登录");
      }
      String userInfoKey = redisUtil.get(loginRedisKey);
      String redisPlatform = getPlatform(userInfoKey);
      xxxxx;
      String userId = getUserId(userInfoKey);
      MDC.put(Constant.HEADER_USER_ID, userId);
      mutate
          .header(Constant.HEADER_USER_ID, userId)
          .header(Constant.HEADER_VERSION, appVersion)
          .header(Constant.HEADER_PLATFORM, appPlatform.name())
          .header(
              Constant.HEADER_USERNAME,
              URLEncodeUtil.encode(redisUtil.hGet(userInfoKey, "username")));
    }
    if (hasRepeatRequest(token, ip, appPlatform, method + uri)) {
      log.info("{}--------------------》重复请求{}", requestId, method + uri);
      return errorReturn(response, requestId, "512", "请稍等一下喔(勿频繁点击)");
    }
    // post请求时,如果是文件上传之类的请求,不修改请求消息体
    exchange.getAttributes().put(Constant.EXCHANGE_HAS_AES, needEncrypt);
    if (needEncrypt) {
      exchange.getAttributes().put(Constant.EXCHANGE_REQUEST_ID, requestId);
      Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
      try {
        if (HttpMethod.GET == method) {
          return encryptParamsGet(
              chain,
              exchange,
              request,
              response,
              mutate,
              requestUri,
              requestId,
              appPlatform,
              copyOfContextMap);
        } else {
          return encryptParamsPost(
              chain, exchange, headers, requestId, appPlatform, copyOfContextMap);
        }
      } catch (Exception e) {
        log.error("{}--------------------》加解密失败:", requestId, e);
        return errorReturn(response, requestId, "512", "请求异常");
      }
    }
    return chain.filter(exchange.mutate().request(request).build());
  }

  private Mono<Void> encryptParamsGet(
      GatewayFilterChain chain,
      ServerWebExchange exchange,
      ServerHttpRequest request,
      ServerHttpResponse response,
      Builder mutate,
      URI requestUri,
      String requestIdValue,
      ProductPlatformEnum appPlatform,
      Map<String, String> copyOfContextMap) {
    MDC.setContextMap(copyOfContextMap);
    Map<String, String> queryParams = request.getQueryParams().toSingleValueMap();
    if (!queryParams.isEmpty() && queryParams.containsKey("v")) {
      String v = queryParams.get("v");
      if (CharSequenceUtil.isBlank(v)) {
        return errorReturn(response, requestIdValue, "512", "请求异常");
      }
      log.info("{}--------------------》原V:{}", requestIdValue, v);
      v = RsaAesUtil.rsaDecryptAesKey(appPlatform, v);
      exchange.getAttributes().put(Constant.EXCHANGE_AES_KEY, v);
      URI uri;
      if (queryParams.containsKey("d")) {
        // 替换查询参数
        String d = queryParams.get("d");
        log.info("{}--------------------》原D:{}", requestIdValue, d);
        String decryptParams =
            CharSequenceUtil.isBlank(d)
                ? ""
                : RsaAesUtil.aesDecryptParams(v.getBytes(StandardCharsets.UTF_8), d);
        log.info("{}--------------------》解D:{}", requestIdValue, decryptParams);
        uri =
            UriComponentsBuilder.fromUri(requestUri)
                .replaceQuery(URLEncodeUtil.encode(decryptParams))
                .build(true)
                .toUri();
      } else {
        uri = UriComponentsBuilder.fromUri(requestUri).replaceQuery("").build(true).toUri();
      }
      ServerHttpRequest build = mutate.uri(uri).build();
      log.info(
          "{}--------------------》重构 {} To {}\n{}",
          requestIdValue,
          build.getMethodValue(),
          build.getURI(),
          build.getHeaders().toSingleValueMap());
      return chain.filter(exchange.mutate().request(build).build());
    } else {
      return errorReturn(response, requestIdValue, "512", "请求异常");
    }
  }

  private Mono<Void> encryptParamsPost(
      GatewayFilterChain chain,
      ServerWebExchange exchange,
      HttpHeaders httpHeaders,
      String requestIdValue,
      ProductPlatformEnum appPlatform,
      Map<String, String> copyOfContextMap) {
    // read & modify body
    MDC.setContextMap(copyOfContextMap);
    ServerRequest serverRequest =
        ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
    Mono<String> modifiedBody =
        serverRequest
            .bodyToMono(String.class)
            .flatMap(
                body -> {
                  MDC.setContextMap(copyOfContextMap);
                  // 对原先的body进行修改操作
                  String str = StrUtil.str(body, StandardCharsets.UTF_8);
                  log.info("{}--------------------》收参:{}", requestIdValue, str);
                  JSONObject jsonObject = JSONUtil.parseObj(str);
                  if (JSONUtil.isNull(jsonObject)) {
                    return Mono.error(new RuntimeException("请求异常"));
                  }
                  if (!jsonObject.containsKey("v")) {
                    return Mono.error(new RuntimeException("请求异常"));
                  }
                  String v = jsonObject.getStr("v");
                  if (CharSequenceUtil.isBlank(v)) {
                    return Mono.error(new RuntimeException("请求异常"));
                  }
                  v = RsaAesUtil.rsaDecryptAesKey(appPlatform, v);
                  exchange.getAttributes().put(Constant.EXCHANGE_AES_KEY, v);
                  if (jsonObject.containsKey("d")) {
                    String d = jsonObject.getStr("d");
                    d =
                        CharSequenceUtil.isBlank(d)
                            ? JSONUtil.toJsonStr(JSONUtil.createObj())
                            : RsaAesUtil.aesDecryptParams(v.getBytes(StandardCharsets.UTF_8), d);
                    log.info("{}--------------------》解D:{}", requestIdValue, d);
                    return Mono.just(d);
                  } else {
                    return Mono.empty();
                  }
                });
    BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
    HttpHeaders headers = new HttpHeaders();
    headers.putAll(httpHeaders);
    headers.remove(HttpHeaders.CONTENT_LENGTH);
    CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
    return bodyInserter
        .insert(outputMessage, new BodyInserterContext())
        .then(
            Mono.defer(
                () -> {
                  MDC.setContextMap(copyOfContextMap);
                  ServerHttpRequestDecorator decorator =
                      new ServerHttpRequestDecorator(exchange.getRequest()) {
                        @Override
                        public HttpHeaders getHeaders() {
                          long contentLength = headers.getContentLength();
                          HttpHeaders httpHeaders = new HttpHeaders();
                          httpHeaders.putAll(super.getHeaders());
                          if (contentLength > 0L) {
                            httpHeaders.setContentLength(contentLength);
                          } else {
                            httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                          }
                          return httpHeaders;
                        }

                        @Override
                        public Flux<DataBuffer> getBody() {
                          return outputMessage.getBody();
                        }
                      };
                  log.info(
                      "{}--------------------》重构 {} To {}\n{}",
                      requestIdValue,
                      decorator.getMethodValue(),
                      decorator.getURI(),
                      decorator.getHeaders().toSingleValueMap());
                  return chain.filter(exchange.mutate().request(decorator).build());
                }));
  }

  private Mono<Void> errorReturn(
      ServerHttpResponse response, String requestId, String code, String msg) {
    if ("302".equals(code)) {
      response.addCookie(
          ResponseCookie.from(Constant.TOKEN, "").path("/").maxAge(Duration.ofSeconds(0L)).build());
    }
    response.getHeaders().set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
    Map<String, String> map = Maps.newHashMapWithExpectedSize(3);
    map.put("code", code);
    map.put("msg", msg);
    map.put("obj", null);
    log.info("{}--------------------》异常返回:{}", requestId, map);
    MDC.clear();
    return response.writeWith(
        Flux.just(response.bufferFactory().wrap(JSON.toJSONString(map).getBytes())));
  }

  /**
   * 是否加解密
   *
   * @param headers
   * @return
   */
  private boolean needEncryptJudgeRequest(HttpHeaders headers, HttpMethod method) {
    return method == HttpMethod.GET
        || (method == HttpMethod.POST
            && (CharSequenceUtil.containsAny(
                headers.getFirst(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JSON_VALUE)));
  }

  /**
   * 检查重复请求
   *
   * @param token 判断唯一请求
   * @param ip 判断唯一请求
   * @param appPlatform 判断唯一请求
   * @param uri
   * @return
   */
  public boolean hasRepeatRequest(
      String token, String ip, ProductPlatformEnum appPlatform, String uri) {
    return xxx;
  }

  private String getPlatform(String userInfoKey) {
    return xxx;
  }

  private String getUserId(String loginRedisValue) {
    return xxx;
  }

  private void asyncUserAgentFun(HttpHeaders headers) {
    log.info(xxx);
  }

  /** 顺序:数字越小,越先执行 */
  @Override
  public int getOrder() {
    return -2;
  }
}

RespFilter

package cn.nocov.hospital.gateway.filter;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.nocov.hospital.gateway.util.RsaAesUtil;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.slf4j.MDC;
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.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author: Zek
 * @date: 2020/8/13 on 4:36 下午
 * @description:
 */
@Slf4j
@Configuration
public class RespFilter implements GlobalFilter, Ordered {

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    Object objHasAes = exchange.getAttributes().get(Constant.EXCHANGE_HAS_AES);
    Boolean hasAes = objHasAes == null ? null : (Boolean) objHasAes;
    String requestIdValue = (String) exchange.getAttributes().get(Constant.EXCHANGE_REQUEST_ID);
    ServerHttpResponse originalResponse = exchange.getResponse();
    originalResponse
        .getHeaders()
        .set(
            Constant.HEADER_REQUEST_ID,
            CharSequenceUtil.isBlank(requestIdValue) ? "" : requestIdValue);
    if (hasAes == null || !hasAes) {
      clearExchange(exchange);
      return chain.filter(exchange.mutate().response(originalResponse).build());
    }
    String key = (String) exchange.getAttributes().get(Constant.EXCHANGE_AES_KEY);
    log.info("{}--------------------》Exchange:{}", requestIdValue, key);
    Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
    DataBufferFactory bufferFactory = originalResponse.bufferFactory();
    ServerHttpResponseDecorator decoratedResponse =
        new ServerHttpResponseDecorator(originalResponse) {
          @Override
          public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
            if (body instanceof Flux) {
              Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
              return super.writeWith(
                  fluxBody
                      .buffer()
                      .map(
                          dataBuffer -> {
                            MDC.setContextMap(copyOfContextMap);
                            DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                            DataBuffer join = dataBufferFactory.join(dataBuffer);
                            byte[] content = new byte[join.readableByteCount()];
                            join.read(content);
                            // 释放掉内存
                            DataBufferUtils.release(join);
                            String respBody = StrUtil.str(content, StandardCharsets.UTF_8);
                            if (!JSONUtil.isTypeJSON(respBody)) {
                              log.error(
                                  "{}--------------------》非JSON:{}", requestIdValue, respBody);
                              return bufferFactory.wrap(content);
                            }
                            JSONObject resp = JSONUtil.parseObj(respBody);
                            String obj = resp.getStr("obj");
                            if (!JSONUtil.isNull(obj)) {
                              resp.set(
                                  "obj",
                                  RsaAesUtil.aesEncryptParams(
                                      key.getBytes(StandardCharsets.UTF_8), obj));
                            }
                            log.info(
                                "{}--------------------》返D:{}",
                                requestIdValue,
                                (respBody.length() > 1000
                                    ? respBody.substring(0, 1000) + "......."
                                    : respBody));
                            // 加密后的数据返回给客户端
                            return bufferFactory.wrap(JSONUtil.toJsonStr(resp).getBytes());
                          }));
            }
            return super.writeWith(body);
          }
        };
    clearExchange(exchange);
    MDC.clear();
    return chain.filter(exchange.mutate().response(decoratedResponse).build());
  }
  /** 顺序:数字越小,越先执行 */
  @Override
  public int getOrder() {
    return -1;
  }

  private void clearExchange(ServerWebExchange exchange) {
    exchange.getAttributes().remove(Constant.EXCHANGE_AES_KEY);
    exchange.getAttributes().remove(Constant.EXCHANGE_HAS_AES);
  }
}

RsaAesUtil

package cn.nocov.hospital.gateway.util;

import cn.hutool.core.lang.Console;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import cn.nocov.hospital.gateway.util.TokenUtil.ProductPlatformEnum;
import javax.crypto.spec.IvParameterSpec;

/**
 * @author: Zek
 * @date: 2022/3/8 on 11:05 AM
 * @desc:
 */
public class RsaAesUtil {

  @Deprecated
  private static final RSA XXXX =
      SecureUtil.rsa(
          "xx",
          "xx");

  /**
   * AES 解密
   *
   * @param aesKey
   * @param aesParams
   * @return
   */
  public static String aesDecryptParams(byte[] aesKey, String aesParams) {
    AES aes = new AES("CBC", "PKCS7Padding", aesKey, new IvParameterSpec(aesKey, 0, 16).getIV());
    return aes.decryptStr(aesParams);
  }

  /**
   * AES 加密
   *
   * @param aesKey
   * @param aesParams
   * @return
   */
  public static String aesEncryptParams(byte[] aesKey, String aesParams) {
    return aes(aesKey).encryptBase64(aesParams);
  }

  /**
   * 获取aes
   *
   * @param aesKey
   * @return
   */
  private static AES aes(byte[] aesKey) {
    return new AES("CBC", "PKCS7Padding", aesKey, new IvParameterSpec(aesKey, 0, 16).getIV());
  }

  /**
   * rsa 解密 aesKey
   *
   * @param appPlatform
   * @param aesKey
   * @return
   */
  public static String rsaDecryptAesKey(ProductPlatformEnum appPlatform, String aesKey) {
    return getRsa(appPlatform).decryptStr(aesKey, KeyType.PrivateKey);
  }

  /**
   * 获取RSA
   *
   * @param appPlatform
   * @return
   */
  private static RSA getRsa(ProductPlatformEnum appPlatform) {
    xxxxxxx
    }
  }

  public static void main(String[] args) {
    RSA rsa = SecureUtil.rsa();
    Console.log(rsa.getPrivateKeyBase64());
    Console.log(rsa.getPublicKeyBase64());
  }
}

效果

 

 问题:

会随机产生HTTP method names must be tokens,下篇文章解决(已解决但是不知道原因);

解决:

Gateway加解密接口HTTP method names must be tokens_邪神大叔的博客-CSDN博客https://blog.csdn.net/qq_17213067/article/details/127859571

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
网关加解密攻击是指攻击者利用漏洞或弱点,对网络中的网关设备进行攻击,以获取加密的数据或篡改数据的过程。这种攻击可能会导致敏感信息泄露、数据篡改、身份伪造等安全问题。 攻击者可以利用多种方式进行网关加解密攻击,包括但不限于以下几种: 1. 中间人攻击:攻击者通过欺骗网关和终端设备之间的通信,将自己置于通信路径中,并以两者之间的代理身份进行通信。这使得攻击者能够获取加密通信中的数据,或者篡改数据。 2. 重放攻击:攻击者截获网络中的加密数据包,并将其重新发送到目标网关。这可能导致网关误认为数据包是合法的,并解密或执行相应的操作。攻击者可以利用这种方式重复发送已知数据包,从而实现攻击目的。 3. 密钥破解:攻击者可能尝试通过暴力破解或其他方式获取网关使用的加密密钥。一旦密钥被破解,攻击者就可以解密和篡改经过网关的加密数据。 为了防止网关加解密攻击,可以采取以下措施: 1. 强化网络安全:定期对网关设备进行漏洞扫描和安全评估,及时修补漏洞和弱点,确保设备的安全性。 2. 密钥管理:使用强密码和密钥管理机制,定期更换密钥,并确保密钥的安全存储和传输。 3. 加密协议选择:选择安全可靠的加密协议,如TLS/SSL等,并配置正确的加密参数,以提高通信的安全性。 4. 监测和检测:部署入侵检测系统(IDS)和入侵防御系统(IPS),实时监测网关设备的网络流量和行为,及时检测并阻止潜在的攻击。 5. 用户教育:加强用户的安全意识和培训,防止社会工程学攻击和钓鱼攻击,减少人为因素对网关安全的影响。 请注意,以上措施只是一些基本建议,并不能完全保证网关的安全。实际应用中,还需要根据具体情况制定更为细化和全面的安全策略。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值