多种设计模式配合使用实现API灵活调用

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值