api调用配置表
-- API 调用配置表
CREATE TABLE api_call_config (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键 ID',
api_name VARCHAR(255) NOT NULL COMMENT 'API 名称',
api_code VARCHAR(100) UNIQUE NOT NULL COMMENT 'API 唯一编码',
api_url VARCHAR(1024) NOT NULL COMMENT 'API 请求地址',
http_method ENUM('GET', 'POST', 'PUT', 'DELETE', 'PATCH') NOT NULL COMMENT 'HTTP 请求方法',
request_format ENUM('JSON', 'FORM', 'XML') DEFAULT 'JSON' COMMENT '请求数据格式',
response_format ENUM('JSON', 'XML', 'TEXT') DEFAULT 'JSON' COMMENT '响应数据格式',
timeout INT DEFAULT 5000 COMMENT '请求超时时间(毫秒)',
retry_times TINYINT DEFAULT 0 COMMENT '重试次数',
auth_type ENUM('NONE', 'BASIC', 'BEARER', 'API_KEY') DEFAULT 'NONE' COMMENT '认证类型',
auth_value VARCHAR(1024) COMMENT '认证信息,如 API Key、Token 等',
headers TEXT COMMENT '请求头信息,JSON 格式存储',
params TEXT COMMENT '请求参数信息,JSON 格式存储',
encryption_algorithm VARCHAR(50) DEFAULT NULL COMMENT '加密算法',
encryption_params VARCHAR(255) DEFAULT NULL COMMENT '加密参数',
encryption_order VARCHAR(255) DEFAULT NULL COMMENT '参数加密顺序,以逗号分隔的参数名列表',
status TINYINT DEFAULT 1 COMMENT 'API 状态,1 启用,0 禁用',
created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='API 调用配置表';
ALTER TABLE api_call_config
ADD COLUMN encrypt_with_param_name TINYINT(1) DEFAULT 0 COMMENT '是否需要参数名和参数值一起加密,1 是,0 否';
定义api配置类
package com.ch.openapi.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Map;
/**
* API 调用配置实体类
*/
@Data
@TableName("api_call_config")
public class ApiCallConfig {
@TableId(type = IdType.AUTO)
private Long id;
private String apiName;
private String apiCode;
private String apiUrl;
private String httpMethod;
private String requestFormat;
private String responseFormat;
private Integer timeout;
private Integer retryTimes;
private String authType;
private String authValue;
private String headers;
private String params;
private Integer status;
private LocalDateTime createdTime;
private LocalDateTime updatedTime;
private String encryptionAlgorithm; // 加密算法
private String encryptionParams; // 加密参数
private String encryptionOrder; // 新增:参数加密顺序,以逗号分隔的参数名列表
}
定义加密算法策略接口
package com.ch.openapi.service;
import java.util.List;
import java.util.Map;
/**
* 加密策略接口,定义加密方法
*/
public interface EncryptionStrategy {
/**
* 执行加密操作
* @param data 需要加密的数据
* @param params 加密所需的参数
* @return 加密后的数据
*/
String encrypt(String data, Map<String, Object> params);
/**
* 按顺序对参数列表进行加密
* @param paramList 需要加密的参数列表
* @param params 加密所需的参数
* @return 加密后的参数列表
*/
List<String> encryptInOrder(List<String> paramList, Map<String, Object> params);
/**
* 按顺序对参数名和值进行加密
* @param paramMap 需要加密的参数键值对
* @param params 加密所需的参数
* @param includeParamName 是否包含参数名
* @return 加密后的参数键值对
*/
Map<String, String> encryptParams(Map<String, String> paramMap, Map<String, Object> params, boolean includeParamName);
}
实现AES加密策略
package com.ch.openapi.service.impl;
import com.ch.openapi.service.EncryptionStrategy;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* AES 加密策略实现类
*/
public class AesEncryptionStrategy implements EncryptionStrategy {
private static final String ALGORITHM = "AES";
@Override
public String encrypt(String data, Map<String, Object> params) {
try {
String key = (String) params.get("encryptionKey");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(data.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
} catch (Exception e) {
throw new RuntimeException("AES encryption failed", e);
}
}
@Override
public List<String> encryptInOrder(List<String> paramList, Map<String, Object> params) {
return paramList.stream()
.map(data -> encrypt(data, params))
.collect(Collectors.toList());
}
@Override
public Map<String, String> encryptParams(Map<String, String> paramMap, Map<String, Object> params, boolean includeParamName) {
Map<String, String> encryptedMap = new HashMap<>();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
String dataToEncrypt = includeParamName ? entry.getKey() + "=" + entry.getValue() : entry.getValue();
encryptedMap.put(entry.getKey(), encrypt(dataToEncrypt, params));
}
return encryptedMap;
}
}
实现MD5加密策略
package com.ch.openapi.service.impl;
import com.ch.openapi.service.EncryptionStrategy;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* MD5 加密策略实现类
*/
public class Md5EncryptionStrategy implements EncryptionStrategy {
@Override
public String encrypt(String data, Map<String, Object> params) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(data.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : messageDigest) {
String hex = Integer.toHexString(0xFF & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException("MD5 encryption failed", e);
}
}
@Override
public List<String> encryptInOrder(List<String> paramList, Map<String, Object> params) {
return paramList.stream()
.map(data -> encrypt(data, params))
.collect(Collectors.toList());
}
@Override
public Map<String, String> encryptParams(Map<String, String> paramMap, Map<String, Object> params, boolean includeParamName) {
Map<String, String> encryptedMap = new HashMap<>();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
String dataToEncrypt = includeParamName ? entry.getKey() + "=" + entry.getValue() : entry.getValue();
encryptedMap.put(entry.getKey(), encrypt(dataToEncrypt, params));
}
return encryptedMap;
}
}
创建一个加密策略工厂
package com.ch.openapi.service;
import com.ch.openapi.service.impl.AesEncryptionStrategy;
import com.ch.openapi.service.impl.Md5EncryptionStrategy;
import java.util.HashMap;
import java.util.Map;
/**
* 加密策略工厂类,根据加密算法名称获取对应的加密策略
*/
public class EncryptionStrategyFactory {
private static final Map<String, EncryptionStrategy> strategies = new HashMap<>();
static {
strategies.put("AES", new AesEncryptionStrategy());
strategies.put("MD5", new Md5EncryptionStrategy());
}
/**
* 根据加密算法名称获取加密策略
* @param algorithm 加密算法名称
* @return 对应的加密策略,若未找到则返回 null
*/
public static EncryptionStrategy getStrategy(String algorithm) {
return strategies.getOrDefault(algorithm, null);
}
}
定义api抽象模板类
package com.ch.openapi.service;
import com.ch.openapi.domain.ApiCallConfig;
import org.springframework.http.ResponseEntity;
import java.util.*;
import java.util.stream.Collectors;
/**
* 抽象 API 调用模板类,定义通用的 API 调用流程
*/
public abstract class AbstractApiCallTemplate {
public final ResponseEntity<?> callApi(ApiCallConfig config, Map<String, Object> params) {
// 检查 API 状态
if (config.getStatus() == 0) {
return ResponseEntity.status(403).body("API is disabled");
}
// 准备请求
prepareRequest(config, params);
// 加密请求数据
Map<String, Object> encryptedParams = encryptRequestData(config, params);
// 执行请求
ResponseEntity<?> response = executeRequest(config, encryptedParams);
// 处理响应
processResponse(response, config);
return response;
}
protected abstract void prepareRequest(ApiCallConfig config, Map<String, Object> params);
/**
* 按指定顺序加密请求数据
* @param config API 调用配置
* @param params 请求参数
* @return 加密后的请求参数
*/
protected Map<String, Object> encryptRequestData(ApiCallConfig config, Map<String, Object> params) {
if (config.getEncryptionAlgorithm() != null && !config.getEncryptionAlgorithm().isEmpty()) {
EncryptionStrategy strategy = EncryptionStrategyFactory.getStrategy(config.getEncryptionAlgorithm());
if (strategy != null) {
if (config.getEncryptionOrder() != null && !config.getEncryptionOrder().isEmpty()) {
List<String> orderList = Arrays.asList(config.getEncryptionOrder().split(","));
List<String> paramValuesInOrder = orderList.stream()
.filter(params::containsKey)
.map(key -> String.valueOf(params.get(key)))
.collect(Collectors.toList());
List<String> encryptedValues = strategy.encryptInOrder(paramValuesInOrder, Map.of("encryptionKey", config.getEncryptionParams()));
// 将加密后的值按顺序放回参数 map
for (int i = 0; i < orderList.size(); i++) {
String key = orderList.get(i);
if (params.containsKey(key)) {
params.put(key, encryptedValues.get(i));
}
}
} else {
// 若没有指定顺序,对所有参数值进行加密
for (Map.Entry<String, Object> entry : params.entrySet()) {
if (entry.getValue() instanceof String) {
String encryptedValue = strategy.encrypt((String) entry.getValue(), Map.of("encryptionKey", config.getEncryptionParams()));
entry.setValue(encryptedValue);
}
}
}
}
}
return params;
}
protected abstract ResponseEntity<?> executeRequest(ApiCallConfig config, Map<String, Object> params);
protected abstract void processResponse(ResponseEntity<?> response, ApiCallConfig config);
}
HttpApiCallTemplate 类继承 AbstractApiCallTemplate,由于加密逻辑已在抽象类实现,这里无需额外处理加密
package com.ch.openapi.service.impl;
import com.ch.openapi.domain.ApiCallConfig;
import com.ch.openapi.service.AbstractApiCallTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import java.util.Map;
/**
* HTTP API 调用模板实现类
*/
public class HttpApiCallTemplate extends AbstractApiCallTemplate {
private static final Logger log = LoggerFactory.getLogger(HttpApiCallTemplate.class);
private final WebClient webClient;
private final ObjectMapper objectMapper;
public HttpApiCallTemplate(WebClient webClient, ObjectMapper objectMapper) {
this.webClient = webClient;
this.objectMapper = objectMapper;
}
@Override
protected void prepareRequest(ApiCallConfig config, Map<String, Object> params) {
// 可添加更多请求准备逻辑,如认证信息处理
log.info("Preparing request for API: {}", config.getApiName());
}
@Override
protected ResponseEntity<?> executeRequest(ApiCallConfig config, Map<String, Object> params) {
try {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 处理认证信息
if ("BEARER".equals(config.getAuthType())) {
headers.setBearerAuth(config.getAuthValue());
}
// 处理请求体
Object requestBody = null;
if ("POST".equalsIgnoreCase(config.getHttpMethod()) || "PUT".equalsIgnoreCase(config.getHttpMethod())) {
requestBody = params;
}
WebClient.RequestBodySpec requestSpec = webClient.method(HttpMethod.valueOf(config.getHttpMethod()))
.uri(config.getApiUrl())
.headers(httpHeaders -> httpHeaders.addAll(headers));
if (requestBody != null) {
requestSpec.bodyValue(requestBody);
}
Mono<ResponseEntity<String>> responseMono = requestSpec.retrieve()
.toEntity(String.class);
return responseMono.block();
} catch (Exception e) {
log.error("Error executing API request: {}", e.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error: " + e.getMessage());
}
}
@Override
protected void processResponse(ResponseEntity<?> response, ApiCallConfig config) {
log.info("Processing response for API: {}", config.getApiName());
log.info("Response status: {}", response.getStatusCode());
log.info("Response body: {}", response.getBody());
}
}
api调用过程中针对多种加密算法,和加密参数顺序可灵活扩展,使用模板模式和策略模式配合使用实现
-- AES 加密,包含参数名和参数值
INSERT INTO api_call_config (
api_name, api_code, api_url, http_method, request_format, response_format,
timeout, retry_times, auth_type, auth_value, headers, params, status,
encryption_algorithm, encryption_params, encryption_order, encrypt_with_param_name
) VALUES (
'AES Encrypt with Param Name',
'aes_with_param_name',
'https://example.com/api/aes-with-name',
'POST',
'JSON',
'JSON',
5000,
2,
'API_KEY',
'your_api_key',
'{"Content-Type": "application/json"}',
'{"param1": "value1", "param2": "value2"}',
1,
'AES',
'your_16bytes_aes_key',
'param1,param2',
1
);
-- MD5 加密,仅包含参数值
INSERT INTO api_call_config (
api_name, api_code, api_url, http_method, request_format, response_format,
timeout, retry_times, auth_type, auth_value, headers, params, status,
encryption_algorithm, encryption_params, encryption_order, encrypt_with_param_name
) VALUES (
'MD5 Encrypt Only Value',
'md5_only_value',
'https://example.com/api/md5-only-value',
'GET',
'FORM',
'JSON',
3000,
1,
'NONE',
NULL,
'{"Accept": "application/json"}',
'{"data": "sensitive_info"}',
1,
'MD5',
NULL,
'data',
0
);
1932

被折叠的 条评论
为什么被折叠?



