• Gateway 接口参数加解密


    方案

    原理:

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

    2. 使用RSA加解密Key;

    具体操作:

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

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

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

    即:

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

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

    ReqFilter

    1. package cn.nocov.hospital.gateway.filter;
    2. import cn.hutool.core.date.DatePattern;
    3. import cn.hutool.core.net.URLEncodeUtil;
    4. import cn.hutool.core.text.CharSequenceUtil;
    5. import cn.hutool.core.thread.ThreadUtil;
    6. import cn.hutool.core.util.ReUtil;
    7. import cn.hutool.core.util.StrUtil;
    8. import cn.hutool.json.JSONObject;
    9. import cn.hutool.json.JSONUtil;
    10. import cn.nocov.hospital.gateway.config.redis.RedisUtil;
    11. import cn.nocov.hospital.gateway.util.IpUtil;
    12. import cn.nocov.hospital.gateway.util.NacosCfgUtil;
    13. import cn.nocov.hospital.gateway.util.RequestUriUtil;
    14. import cn.nocov.hospital.gateway.util.RsaAesUtil;
    15. import cn.nocov.hospital.gateway.util.TokenUtil;
    16. import cn.nocov.hospital.gateway.util.TokenUtil.ProductPlatformEnum;
    17. import com.alibaba.fastjson.JSON;
    18. import com.google.common.collect.Maps;
    19. import java.net.URI;
    20. import java.nio.charset.StandardCharsets;
    21. import java.time.Duration;
    22. import java.time.LocalDateTime;
    23. import java.util.Map;
    24. import java.util.concurrent.TimeUnit;
    25. import javax.annotation.Resource;
    26. import lombok.extern.slf4j.Slf4j;
    27. import org.slf4j.MDC;
    28. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    29. import org.springframework.cloud.gateway.filter.GlobalFilter;
    30. import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
    31. import org.springframework.cloud.gateway.support.BodyInserterContext;
    32. import org.springframework.context.annotation.Configuration;
    33. import org.springframework.core.Ordered;
    34. import org.springframework.core.io.buffer.DataBuffer;
    35. import org.springframework.http.HttpHeaders;
    36. import org.springframework.http.HttpMethod;
    37. import org.springframework.http.MediaType;
    38. import org.springframework.http.ResponseCookie;
    39. import org.springframework.http.server.reactive.ServerHttpRequest;
    40. import org.springframework.http.server.reactive.ServerHttpRequest.Builder;
    41. import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
    42. import org.springframework.http.server.reactive.ServerHttpResponse;
    43. import org.springframework.web.bind.annotation.RequestMethod;
    44. import org.springframework.web.reactive.function.BodyInserter;
    45. import org.springframework.web.reactive.function.BodyInserters;
    46. import org.springframework.web.reactive.function.server.HandlerStrategies;
    47. import org.springframework.web.reactive.function.server.ServerRequest;
    48. import org.springframework.web.server.ServerWebExchange;
    49. import org.springframework.web.util.UriComponentsBuilder;
    50. import reactor.core.publisher.Flux;
    51. import reactor.core.publisher.Mono;
    52. /**
    53. * @author: Zek
    54. * @date: 2020/8/13 on 4:36 下午
    55. * @description: 请求过滤器,注意:@Order、Ordered虽然都是执行顺序,但是使用@Order注解会导致url少一位,不知道啥问题
    56. */
    57. @Slf4j
    58. @Configuration
    59. public class ReqFilter implements GlobalFilter, Ordered {
    60. @Resource private NacosCfgUtil nacosCfgUtil;
    61. @Resource private RedisUtil redisUtil;
    62. @Resource private RequestUriUtil requestUriUtil;
    63. @Override
    64. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    65. MDC.clear();
    66. ServerHttpRequest request = exchange.getRequest();
    67. ServerHttpResponse response = exchange.getResponse();
    68. if (RequestMethod.OPTIONS.name().equals(request.getMethodValue())) {
    69. return chain.filter(exchange);
    70. }
    71. URI requestUri = request.getURI();
    72. String uri = requestUri.getPath();
    73. String url = requestUri.toString();
    74. HttpHeaders headers = request.getHeaders();
    75. ProductPlatformEnum appPlatform =
    76. ProductPlatformEnum.toEnum(headers.getFirst(Constant.HEADER_PLATFORM));
    77. MDC.put(Constant.HEADER_PLATFORM, appPlatform == null ? "" : appPlatform.name());
    78. String appVersion = headers.getFirst(Constant.HEADER_VERSION);
    79. MDC.put(Constant.HEADER_VERSION, appVersion);
    80. String requestId = headers.getFirst(Constant.HEADER_REQUEST_ID);
    81. MDC.put(Constant.HEADER_REQUEST_ID, requestId);
    82. HttpMethod method = request.getMethod();
    83. log.info(
    84. "{}--------------------》Send {} To {}\n{}",
    85. requestId,
    86. method,
    87. url,
    88. headers.toSingleValueMap());
    89. boolean needEncrypt = requestUriUtil.noTokenNeedEncrypt(uri);
    90. if (needEncrypt) {
    91. // 判断请求方式是否需要加密
    92. needEncrypt = needEncryptJudgeRequest(headers, method);
    93. if (needEncrypt && !TokenUtil.checkHeader(appPlatform, appVersion, requestId)) {
    94. return errorReturn(response, requestId, "512", "版本过低,请前往商店更新最新版本");
    95. }
    96. } else {
    97. if (CharSequenceUtil.isNotBlank(requestId)) {
    98. requestId = TokenUtil.getRequestId();
    99. }
    100. }
    101. if (CharSequenceUtil.isNotBlank(requestId)) {
    102. response.getHeaders().set(Constant.HEADER_REQUEST_ID, requestId);
    103. }
    104. String ip = IpUtil.getIp(request);
    105. Builder mutate =
    106. request
    107. .mutate()
    108. .header(Constant.HEADER_IP, ip)
    109. .header(Constant.HEADER_REQUEST_ID, requestId);
    110. // 需要校验token的接口
    111. String token = "";
    112. if (requestUriUtil.needToken(uri)) {
    113. if (appPlatform == null) {
    114. log.info("{}--------------------》登录校验失败:appPlatform == null,url:{}", requestId, url);
    115. return errorReturn(response, requestId, "302", "登录验证失败,请重新登录");
    116. }
    117. token = TokenUtil.getToken(request, appPlatform);
    118. if (CharSequenceUtil.isBlank(token)) {
    119. log.info("{}--------------------》登录校验失败:cookie token == null/'',url:{}", requestId, url);
    120. return errorReturn(response, requestId, "302", "登录验证失败,请重新登录");
    121. }
    122. String userInfoKey = redisUtil.get(loginRedisKey);
    123. String redisPlatform = getPlatform(userInfoKey);
    124. xxxxx;
    125. String userId = getUserId(userInfoKey);
    126. MDC.put(Constant.HEADER_USER_ID, userId);
    127. mutate
    128. .header(Constant.HEADER_USER_ID, userId)
    129. .header(Constant.HEADER_VERSION, appVersion)
    130. .header(Constant.HEADER_PLATFORM, appPlatform.name())
    131. .header(
    132. Constant.HEADER_USERNAME,
    133. URLEncodeUtil.encode(redisUtil.hGet(userInfoKey, "username")));
    134. }
    135. if (hasRepeatRequest(token, ip, appPlatform, method + uri)) {
    136. log.info("{}--------------------》重复请求{}", requestId, method + uri);
    137. return errorReturn(response, requestId, "512", "请稍等一下喔(勿频繁点击)");
    138. }
    139. // post请求时,如果是文件上传之类的请求,不修改请求消息体
    140. exchange.getAttributes().put(Constant.EXCHANGE_HAS_AES, needEncrypt);
    141. if (needEncrypt) {
    142. exchange.getAttributes().put(Constant.EXCHANGE_REQUEST_ID, requestId);
    143. Map copyOfContextMap = MDC.getCopyOfContextMap();
    144. try {
    145. if (HttpMethod.GET == method) {
    146. return encryptParamsGet(
    147. chain,
    148. exchange,
    149. request,
    150. response,
    151. mutate,
    152. requestUri,
    153. requestId,
    154. appPlatform,
    155. copyOfContextMap);
    156. } else {
    157. return encryptParamsPost(
    158. chain, exchange, headers, requestId, appPlatform, copyOfContextMap);
    159. }
    160. } catch (Exception e) {
    161. log.error("{}--------------------》加解密失败:", requestId, e);
    162. return errorReturn(response, requestId, "512", "请求异常");
    163. }
    164. }
    165. return chain.filter(exchange.mutate().request(request).build());
    166. }
    167. private Mono encryptParamsGet(
    168. GatewayFilterChain chain,
    169. ServerWebExchange exchange,
    170. ServerHttpRequest request,
    171. ServerHttpResponse response,
    172. Builder mutate,
    173. URI requestUri,
    174. String requestIdValue,
    175. ProductPlatformEnum appPlatform,
    176. Map copyOfContextMap) {
    177. MDC.setContextMap(copyOfContextMap);
    178. Map queryParams = request.getQueryParams().toSingleValueMap();
    179. if (!queryParams.isEmpty() && queryParams.containsKey("v")) {
    180. String v = queryParams.get("v");
    181. if (CharSequenceUtil.isBlank(v)) {
    182. return errorReturn(response, requestIdValue, "512", "请求异常");
    183. }
    184. log.info("{}--------------------》原V:{}", requestIdValue, v);
    185. v = RsaAesUtil.rsaDecryptAesKey(appPlatform, v);
    186. exchange.getAttributes().put(Constant.EXCHANGE_AES_KEY, v);
    187. URI uri;
    188. if (queryParams.containsKey("d")) {
    189. // 替换查询参数
    190. String d = queryParams.get("d");
    191. log.info("{}--------------------》原D:{}", requestIdValue, d);
    192. String decryptParams =
    193. CharSequenceUtil.isBlank(d)
    194. ? ""
    195. : RsaAesUtil.aesDecryptParams(v.getBytes(StandardCharsets.UTF_8), d);
    196. log.info("{}--------------------》解D:{}", requestIdValue, decryptParams);
    197. uri =
    198. UriComponentsBuilder.fromUri(requestUri)
    199. .replaceQuery(URLEncodeUtil.encode(decryptParams))
    200. .build(true)
    201. .toUri();
    202. } else {
    203. uri = UriComponentsBuilder.fromUri(requestUri).replaceQuery("").build(true).toUri();
    204. }
    205. ServerHttpRequest build = mutate.uri(uri).build();
    206. log.info(
    207. "{}--------------------》重构 {} To {}\n{}",
    208. requestIdValue,
    209. build.getMethodValue(),
    210. build.getURI(),
    211. build.getHeaders().toSingleValueMap());
    212. return chain.filter(exchange.mutate().request(build).build());
    213. } else {
    214. return errorReturn(response, requestIdValue, "512", "请求异常");
    215. }
    216. }
    217. private Mono encryptParamsPost(
    218. GatewayFilterChain chain,
    219. ServerWebExchange exchange,
    220. HttpHeaders httpHeaders,
    221. String requestIdValue,
    222. ProductPlatformEnum appPlatform,
    223. Map copyOfContextMap) {
    224. // read & modify body
    225. MDC.setContextMap(copyOfContextMap);
    226. ServerRequest serverRequest =
    227. ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
    228. Mono modifiedBody =
    229. serverRequest
    230. .bodyToMono(String.class)
    231. .flatMap(
    232. body -> {
    233. MDC.setContextMap(copyOfContextMap);
    234. // 对原先的body进行修改操作
    235. String str = StrUtil.str(body, StandardCharsets.UTF_8);
    236. log.info("{}--------------------》收参:{}", requestIdValue, str);
    237. JSONObject jsonObject = JSONUtil.parseObj(str);
    238. if (JSONUtil.isNull(jsonObject)) {
    239. return Mono.error(new RuntimeException("请求异常"));
    240. }
    241. if (!jsonObject.containsKey("v")) {
    242. return Mono.error(new RuntimeException("请求异常"));
    243. }
    244. String v = jsonObject.getStr("v");
    245. if (CharSequenceUtil.isBlank(v)) {
    246. return Mono.error(new RuntimeException("请求异常"));
    247. }
    248. v = RsaAesUtil.rsaDecryptAesKey(appPlatform, v);
    249. exchange.getAttributes().put(Constant.EXCHANGE_AES_KEY, v);
    250. if (jsonObject.containsKey("d")) {
    251. String d = jsonObject.getStr("d");
    252. d =
    253. CharSequenceUtil.isBlank(d)
    254. ? JSONUtil.toJsonStr(JSONUtil.createObj())
    255. : RsaAesUtil.aesDecryptParams(v.getBytes(StandardCharsets.UTF_8), d);
    256. log.info("{}--------------------》解D:{}", requestIdValue, d);
    257. return Mono.just(d);
    258. } else {
    259. return Mono.empty();
    260. }
    261. });
    262. BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
    263. HttpHeaders headers = new HttpHeaders();
    264. headers.putAll(httpHeaders);
    265. headers.remove(HttpHeaders.CONTENT_LENGTH);
    266. CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
    267. return bodyInserter
    268. .insert(outputMessage, new BodyInserterContext())
    269. .then(
    270. Mono.defer(
    271. () -> {
    272. MDC.setContextMap(copyOfContextMap);
    273. ServerHttpRequestDecorator decorator =
    274. new ServerHttpRequestDecorator(exchange.getRequest()) {
    275. @Override
    276. public HttpHeaders getHeaders() {
    277. long contentLength = headers.getContentLength();
    278. HttpHeaders httpHeaders = new HttpHeaders();
    279. httpHeaders.putAll(super.getHeaders());
    280. if (contentLength > 0L) {
    281. httpHeaders.setContentLength(contentLength);
    282. } else {
    283. httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
    284. }
    285. return httpHeaders;
    286. }
    287. @Override
    288. public Flux getBody() {
    289. return outputMessage.getBody();
    290. }
    291. };
    292. log.info(
    293. "{}--------------------》重构 {} To {}\n{}",
    294. requestIdValue,
    295. decorator.getMethodValue(),
    296. decorator.getURI(),
    297. decorator.getHeaders().toSingleValueMap());
    298. return chain.filter(exchange.mutate().request(decorator).build());
    299. }));
    300. }
    301. private Mono errorReturn(
    302. ServerHttpResponse response, String requestId, String code, String msg) {
    303. if ("302".equals(code)) {
    304. response.addCookie(
    305. ResponseCookie.from(Constant.TOKEN, "").path("/").maxAge(Duration.ofSeconds(0L)).build());
    306. }
    307. response.getHeaders().set(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
    308. Map map = Maps.newHashMapWithExpectedSize(3);
    309. map.put("code", code);
    310. map.put("msg", msg);
    311. map.put("obj", null);
    312. log.info("{}--------------------》异常返回:{}", requestId, map);
    313. MDC.clear();
    314. return response.writeWith(
    315. Flux.just(response.bufferFactory().wrap(JSON.toJSONString(map).getBytes())));
    316. }
    317. /**
    318. * 是否加解密
    319. *
    320. * @param headers
    321. * @return
    322. */
    323. private boolean needEncryptJudgeRequest(HttpHeaders headers, HttpMethod method) {
    324. return method == HttpMethod.GET
    325. || (method == HttpMethod.POST
    326. && (CharSequenceUtil.containsAny(
    327. headers.getFirst(HttpHeaders.CONTENT_TYPE), MediaType.APPLICATION_JSON_VALUE)));
    328. }
    329. /**
    330. * 检查重复请求
    331. *
    332. * @param token 判断唯一请求
    333. * @param ip 判断唯一请求
    334. * @param appPlatform 判断唯一请求
    335. * @param uri
    336. * @return
    337. */
    338. public boolean hasRepeatRequest(
    339. String token, String ip, ProductPlatformEnum appPlatform, String uri) {
    340. return xxx;
    341. }
    342. private String getPlatform(String userInfoKey) {
    343. return xxx;
    344. }
    345. private String getUserId(String loginRedisValue) {
    346. return xxx;
    347. }
    348. private void asyncUserAgentFun(HttpHeaders headers) {
    349. log.info(xxx);
    350. }
    351. /** 顺序:数字越小,越先执行 */
    352. @Override
    353. public int getOrder() {
    354. return -2;
    355. }
    356. }

    RespFilter

    1. package cn.nocov.hospital.gateway.filter;
    2. import cn.hutool.core.text.CharSequenceUtil;
    3. import cn.hutool.core.util.StrUtil;
    4. import cn.hutool.json.JSONObject;
    5. import cn.hutool.json.JSONUtil;
    6. import cn.nocov.hospital.gateway.util.RsaAesUtil;
    7. import java.nio.charset.StandardCharsets;
    8. import java.util.Map;
    9. import lombok.extern.slf4j.Slf4j;
    10. import org.reactivestreams.Publisher;
    11. import org.slf4j.MDC;
    12. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    13. import org.springframework.cloud.gateway.filter.GlobalFilter;
    14. import org.springframework.context.annotation.Configuration;
    15. import org.springframework.core.Ordered;
    16. import org.springframework.core.io.buffer.DataBuffer;
    17. import org.springframework.core.io.buffer.DataBufferFactory;
    18. import org.springframework.core.io.buffer.DataBufferUtils;
    19. import org.springframework.core.io.buffer.DefaultDataBufferFactory;
    20. import org.springframework.http.server.reactive.ServerHttpResponse;
    21. import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
    22. import org.springframework.web.server.ServerWebExchange;
    23. import reactor.core.publisher.Flux;
    24. import reactor.core.publisher.Mono;
    25. /**
    26. * @author: Zek
    27. * @date: 2020/8/13 on 4:36 下午
    28. * @description:
    29. */
    30. @Slf4j
    31. @Configuration
    32. public class RespFilter implements GlobalFilter, Ordered {
    33. @Override
    34. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    35. Object objHasAes = exchange.getAttributes().get(Constant.EXCHANGE_HAS_AES);
    36. Boolean hasAes = objHasAes == null ? null : (Boolean) objHasAes;
    37. String requestIdValue = (String) exchange.getAttributes().get(Constant.EXCHANGE_REQUEST_ID);
    38. ServerHttpResponse originalResponse = exchange.getResponse();
    39. originalResponse
    40. .getHeaders()
    41. .set(
    42. Constant.HEADER_REQUEST_ID,
    43. CharSequenceUtil.isBlank(requestIdValue) ? "" : requestIdValue);
    44. if (hasAes == null || !hasAes) {
    45. clearExchange(exchange);
    46. return chain.filter(exchange.mutate().response(originalResponse).build());
    47. }
    48. String key = (String) exchange.getAttributes().get(Constant.EXCHANGE_AES_KEY);
    49. log.info("{}--------------------》Exchange:{}", requestIdValue, key);
    50. Map copyOfContextMap = MDC.getCopyOfContextMap();
    51. DataBufferFactory bufferFactory = originalResponse.bufferFactory();
    52. ServerHttpResponseDecorator decoratedResponse =
    53. new ServerHttpResponseDecorator(originalResponse) {
    54. @Override
    55. public Mono writeWith(Publisher body) {
    56. if (body instanceof Flux) {
    57. Fluxextends DataBuffer> fluxBody = (Fluxextends DataBuffer>) body;
    58. return super.writeWith(
    59. fluxBody
    60. .buffer()
    61. .map(
    62. dataBuffer -> {
    63. MDC.setContextMap(copyOfContextMap);
    64. DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
    65. DataBuffer join = dataBufferFactory.join(dataBuffer);
    66. byte[] content = new byte[join.readableByteCount()];
    67. join.read(content);
    68. // 释放掉内存
    69. DataBufferUtils.release(join);
    70. String respBody = StrUtil.str(content, StandardCharsets.UTF_8);
    71. if (!JSONUtil.isTypeJSON(respBody)) {
    72. log.error(
    73. "{}--------------------》非JSON:{}", requestIdValue, respBody);
    74. return bufferFactory.wrap(content);
    75. }
    76. JSONObject resp = JSONUtil.parseObj(respBody);
    77. String obj = resp.getStr("obj");
    78. if (!JSONUtil.isNull(obj)) {
    79. resp.set(
    80. "obj",
    81. RsaAesUtil.aesEncryptParams(
    82. key.getBytes(StandardCharsets.UTF_8), obj));
    83. }
    84. log.info(
    85. "{}--------------------》返D:{}",
    86. requestIdValue,
    87. (respBody.length() > 1000
    88. ? respBody.substring(0, 1000) + "......."
    89. : respBody));
    90. // 加密后的数据返回给客户端
    91. return bufferFactory.wrap(JSONUtil.toJsonStr(resp).getBytes());
    92. }));
    93. }
    94. return super.writeWith(body);
    95. }
    96. };
    97. clearExchange(exchange);
    98. MDC.clear();
    99. return chain.filter(exchange.mutate().response(decoratedResponse).build());
    100. }
    101. /** 顺序:数字越小,越先执行 */
    102. @Override
    103. public int getOrder() {
    104. return -1;
    105. }
    106. private void clearExchange(ServerWebExchange exchange) {
    107. exchange.getAttributes().remove(Constant.EXCHANGE_AES_KEY);
    108. exchange.getAttributes().remove(Constant.EXCHANGE_HAS_AES);
    109. }
    110. }

    RsaAesUtil

    1. package cn.nocov.hospital.gateway.util;
    2. import cn.hutool.core.lang.Console;
    3. import cn.hutool.crypto.SecureUtil;
    4. import cn.hutool.crypto.asymmetric.KeyType;
    5. import cn.hutool.crypto.asymmetric.RSA;
    6. import cn.hutool.crypto.symmetric.AES;
    7. import cn.nocov.hospital.gateway.util.TokenUtil.ProductPlatformEnum;
    8. import javax.crypto.spec.IvParameterSpec;
    9. /**
    10. * @author: Zek
    11. * @date: 2022/3/8 on 11:05 AM
    12. * @desc:
    13. */
    14. public class RsaAesUtil {
    15. @Deprecated
    16. private static final RSA XXXX =
    17. SecureUtil.rsa(
    18. "xx",
    19. "xx");
    20. /**
    21. * AES 解密
    22. *
    23. * @param aesKey
    24. * @param aesParams
    25. * @return
    26. */
    27. public static String aesDecryptParams(byte[] aesKey, String aesParams) {
    28. AES aes = new AES("CBC", "PKCS7Padding", aesKey, new IvParameterSpec(aesKey, 0, 16).getIV());
    29. return aes.decryptStr(aesParams);
    30. }
    31. /**
    32. * AES 加密
    33. *
    34. * @param aesKey
    35. * @param aesParams
    36. * @return
    37. */
    38. public static String aesEncryptParams(byte[] aesKey, String aesParams) {
    39. return aes(aesKey).encryptBase64(aesParams);
    40. }
    41. /**
    42. * 获取aes
    43. *
    44. * @param aesKey
    45. * @return
    46. */
    47. private static AES aes(byte[] aesKey) {
    48. return new AES("CBC", "PKCS7Padding", aesKey, new IvParameterSpec(aesKey, 0, 16).getIV());
    49. }
    50. /**
    51. * rsa 解密 aesKey
    52. *
    53. * @param appPlatform
    54. * @param aesKey
    55. * @return
    56. */
    57. public static String rsaDecryptAesKey(ProductPlatformEnum appPlatform, String aesKey) {
    58. return getRsa(appPlatform).decryptStr(aesKey, KeyType.PrivateKey);
    59. }
    60. /**
    61. * 获取RSA
    62. *
    63. * @param appPlatform
    64. * @return
    65. */
    66. private static RSA getRsa(ProductPlatformEnum appPlatform) {
    67. xxxxxxx
    68. }
    69. }
    70. public static void main(String[] args) {
    71. RSA rsa = SecureUtil.rsa();
    72. Console.log(rsa.getPrivateKeyBase64());
    73. Console.log(rsa.getPublicKeyBase64());
    74. }
    75. }

    效果

     

     问题:

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

    解决:

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

  • 相关阅读:
    原来栈和队列之间其实也是可以互相转换的啊
    猴子也能学会的jQuery第一期——什么是jQuery
    计算机视觉中的自监督学习
    面试五 -bind 和 function
    关于Comparable、Comparator接口返回值决定顺序的问题
    二、工业方案推荐系统
    每日一题 1143最长公共子序列(LCS)(灵神版本)
    初阶数据结构学习记录——아홉 二叉树和堆(2)
    “Vue进阶:深入理解插值、指令、过滤器、计算属性和监听器“
    web:[极客大挑战 2019]PHP
  • 原文地址:https://blog.csdn.net/qq_17213067/article/details/127859326