springboot三方系统接口调用

1.提供方接口

@RestController
public class AiAuthService {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate; // 用于存储已使用的nonce
    
    @Value("${ai.secretKey}") // 假设服务器有预设的密钥
    private String secretKey;
    
    @PostMapping("/auth/token")
    public ResponseEntity<AiApiResponse<String>> authenticate(@RequestBody AiAuthRequest request) {
        try {
            // 1. 检查必需参数
            if (request.getClientId() == null || request.getTimestamp() == null || 
                request.getNonce() == null || request.getSign() == null) {
                return ResponseEntity.ok(AiApiResponse.error("缺少必需参数"));
            }
            
            // 2. 校验clientId是否有效
            if (!isValidClientId(request.getClientId())) {
                return ResponseEntity.ok(AiApiResponse.error("无效的客户端ID"));
            }
            
            // 3. 校验时间戳(防止重放攻击,比如时间戳不能超过5分钟)
            long currentTime = System.currentTimeMillis();
            if (Math.abs(currentTime - request.getTimestamp()) > 5 * 60 * 1000) { // 5分钟
                return ResponseEntity.ok(AiApiResponse.error("时间戳过期"));
            }
            
            // 4. 校验nonce是否已使用(防止重放攻击)
            String nonceKey = "ai_nonce:" + request.getNonce();
            if (redisTemplate.hasKey(nonceKey)) {
                return ResponseEntity.ok(AiApiResponse.error("随机数已使用"));
            }
            // 将nonce存入缓存,设置过期时间
            redisTemplate.opsForValue().set(nonceKey, "used", 5, TimeUnit.MINUTES);
            
            // 5. 校验签名
            String expectedSign = SignUtils.md5("clientId=" + request.getClientId() + 
                                              "&nonce=" + request.getNonce() + 
                                              "&timestamp=" + request.getTimestamp());
            if (!expectedSign.equals(request.getSign())) {
                return ResponseEntity.ok(AiApiResponse.error("签名验证失败"));
            }
            
            // 6. 所有校验通过,生成并返回token或URL
            String token = generateToken(request.getClientId());
            return ResponseEntity.ok(AiApiResponse.success(token));
            
        } catch (Exception e) {
            return ResponseEntity.ok(AiApiResponse.error("认证失败: " + e.getMessage()));
        }
    }
    
    private boolean isValidClientId(String clientId) {
        // 实际实现可能是查询数据库或配置
        return "valid_client_id".equals(clientId); // 示例
    }
    
    private String generateToken(String clientId) {
        // 实际实现可能生成JWT或其他类型的token
        return "generated_token_for_" + clientId;
    }
}

2.调用方接口

@Slf4j
@Service
public class AiAuthRestTemplateService {

    @Value("${ai.clientId}")
    private String clientId;

    @Value("${ai.url}")
    private String aiUrl;

    @Value("${ai.webIp}")
    private String webUrl;

    @Value("${ai.devIp}")
    private String devIp;

    private final RestTemplate restTemplate;

    public AiAuthRestTemplateService() {
        this.restTemplate = new RestTemplate();
        // 设置SSL忽略
        disableSSLValidation();
    }

    public String getAuthToken() {
        Long timestamp = System.currentTimeMillis();
        String nonce = IdWorker.get32UUID().substring(0, 12);
        // 签名规则
        String sign = SignUtils.md5("clientId=" + clientId + "&nonce=" + nonce + "&timestamp=" + timestamp);
        AiAuthRequest request = AiAuthRequest.builder()
                .clientId(clientId)
                .timestamp(timestamp)
                .nonce(nonce)
                .sign(sign)
                .build();
        log.info("AI智能体URL---:{}", aiUrl);
        try {
            ResponseEntity<AiApiResponse<String>> response = restTemplate.exchange(
                    aiUrl,
                    HttpMethod.POST,
                    new HttpEntity<>(request),
                    new ParameterizedTypeReference<AiApiResponse<String>>() {
                    }
            );
            AiApiResponse<String> responseBody = response.getBody();
            if (responseBody == null && !"000000".equals(responseBody.getCode())) {
                return "获取AI智能体数据失败:" + responseBody.getMessage();
            }
            if (responseBody.getData().contains(devIp)) {
                String webAddres = responseBody.getData().replace(devIp, webUrl);
                log.info("ai智能体前端访问的URL:{}", webAddres);
                return webAddres;
            }
            return responseBody.getData();
        } catch (Exception e) {
            return "AI智能体调用网络故障---:" + e.getMessage();
        }
    }

    private void disableSSLValidation() {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            }}, new SecureRandom());

            SSLSocketFactory factory = sslContext.getSocketFactory();
            HttpsURLConnection.setDefaultSSLSocketFactory(factory);
            HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
        } catch (Exception e) {
            throw new RuntimeException("禁用SSL验证失败", e);
        }
    }

}

3.MD5加密工具

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class SignUtils {

    /**
     * 使用JDK自带的MessageDigest实现MD5加密
     *
     * @param data 待加密的数据
     * @return MD5加密后的小写十六进制字符串
     */
    public static String md5(String data) {
        // 检查字符串
        if (data == null || data.isEmpty()) {
            throw new IllegalArgumentException("SignUtils md5 Input data cannot be null or empty");
        }
        try {
            // 获取MD5实例
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 使用标准UTF-8编码处理字符串,这是跨语言兼容的关键
            byte[] digest = md.digest(data.getBytes(StandardCharsets.UTF_8));

            // 使用传统的十六进制转换方法,确保格式一致性
            StringBuilder hexString = new StringBuilder(32); // MD5固定32位
            for (byte b : digest) {
                // 将字节转换为十六进制,确保始终是2位小写字母
                hexString.append(String.format("%02x", b & 0xff));
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SignUtils md5 algorithm not available", e);
        }
    }

    public static void main(String[] args) {
        String data = "撒大大";
        System.out.println(md5(data));
    }

}

 

posted @ 2025-12-24 09:24  敲代码的机车Boy  阅读(1)  评论(0)    收藏  举报