Java案例如何实现接口签名?

wen python案例 2

本文目录导读:

Java案例如何实现接口签名?

  1. 什么是接口签名
  2. 基本实现思路
  3. 完整实现示例
  4. Spring Boot集成实现
  5. Controller层使用
  6. 增强功能实现
  7. 最佳实践建议

我来详细介绍Java中实现接口签名的几种常见方法。

什么是接口签名

接口签名是通过对请求参数进行加密处理,生成一段校验码,用于验证请求的合法性和完整性,防止数据被篡改。

基本实现思路

// 签名生成流程
1. 收集所有请求参数
2. 按参数名排序
3. 拼接成字符串
4. 加上密钥
5. 进行加密(MD5/SHA等)
6. 生成签名

完整实现示例

1 工具类实现

import java.security.MessageDigest;
import java.util.*;
public class SignUtil {
    /**
     * 生成签名
     * @param params 请求参数Map
     * @param secret 密钥
     * @return 签名串
     */
    public static String generateSign(Map<String, String> params, String secret) {
        // 1. 移除签名本身(防止重复计算)
        params.remove("sign");
        // 2. 获取所有参数名并排序
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        // 3. 拼接参数
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            String value = params.get(key);
            if (value != null && !value.isEmpty()) {
                sb.append(key).append("=").append(value).append("&");
            }
        }
        // 4. 添加密钥
        sb.append("key=").append(secret);
        // 5. MD5加密
        return md5(sb.toString()).toUpperCase();
    }
    /**
     * 验证签名
     * @param params 请求参数
     * @param secret 密钥
     * @return 是否验证通过
     */
    public static boolean verifySign(Map<String, String> params, String secret) {
        String sign = params.get("sign");
        if (sign == null || sign.isEmpty()) {
            return false;
        }
        String calSign = generateSign(params, secret);
        return calSign.equals(sign);
    }
    /**
     * MD5加密
     */
    private static String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes("UTF-8"));
            StringBuilder sb = new StringBuilder();
            for (byte b : digest) {
                sb.append(String.format("%02x", b & 0xff));
            }
            return sb.toString();
        } catch (Exception e) {
            throw new RuntimeException("MD5加密失败", e);
        }
    }
}

2 使用示例

public class SignDemo {
    public static void main(String[] args) {
        // 1. 请求参数
        Map<String, String> params = new HashMap<>();
        params.put("appId", "10001");
        params.put("timestamp", "1695102400");
        params.put("nonce", "random123");
        params.put("data", "{\"name\":\"张三\",\"age\":25}");
        // 2. 密钥(实际应用中应该从配置文件或密钥服务器获取)
        String secret = "your-secret-key-123456";
        // 3. 生成签名
        String sign = SignUtil.generateSign(params, secret);
        params.put("sign", sign);
        System.out.println("签名结果: " + sign);
        // 4. 验证签名
        boolean isValid = SignUtil.verifySign(params, secret);
        System.out.println("签名验证: " + (isValid ? "通过" : "失败"));
    }
}

Spring Boot集成实现

1 自定义注解

import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface VerifySign {
    // 是否需要验证签名
    boolean required() default true;
}

2 拦截器实现

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
public class SignInterceptor implements HandlerInterceptor {
    private final String secret;
    public SignInterceptor(String secret) {
        this.secret = secret;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) throws Exception {
        // 获取请求参数
        Map<String, String> params = getParams(request);
        // 验证签名
        if (!SignUtil.verifySign(params, secret)) {
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"msg\":\"签名验证失败\"}");
            return false;
        }
        return true;
    }
    private Map<String, String> getParams(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        Enumeration<String> names = request.getParameterNames();
        while (names.hasMoreElements()) {
            String name = names.nextElement();
            params.put(name, request.getParameter(name));
        }
        return params;
    }
}

3 配置拦截器

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SignInterceptor("your-secret-key"))
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/public/**");
    }
}

Controller层使用

import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class UserController {
    @PostMapping("/user/create")
    @VerifySign
    public Result createUser(@RequestParam Map<String, String> params) {
        // 业务逻辑
        return Result.success();
    }
}

增强功能实现

1 防重放攻击

public class AdvancedSignUtil extends SignUtil {
    private static final long TIMEOUT = 5 * 60 * 1000; // 5分钟超时
    public static boolean verifySignWithNonce(Map<String, String> params, String secret) {
        // 1. 验证签名
        if (!verifySign(params, secret)) {
            return false;
        }
        // 2. 验证时间戳
        String timestamp = params.get("timestamp");
        if (timestamp == null) {
            return false;
        }
        long time = Long.parseLong(timestamp);
        if (Math.abs(System.currentTimeMillis() - time * 1000) > TIMEOUT) {
            return false; // 请求超时
        }
        // 3. 验证nonce(防止重放)
        String nonce = params.get("nonce");
        if (nonce == null || isNonceUsed(nonce)) {
            return false;
        }
        saveNonce(nonce);
        return true;
    }
    // 实际应用中应该使用Redis存储
    private static Set<String> usedNonces = new HashSet<>();
    private static boolean isNonceUsed(String nonce) {
        return usedNonces.contains(nonce);
    }
    private static void saveNonce(String nonce) {
        usedNonces.add(nonce);
    }
}

2 客户端示例

public class ApiClient {
    private String appId;
    private String secret;
    public ApiClient(String appId, String secret) {
        this.appId = appId;
        this.secret = secret;
    }
    public Map<String, String> buildRequest(Map<String, String> data) {
        Map<String, String> params = new HashMap<>();
        params.put("appId", appId);
        params.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
        params.put("nonce", UUID.randomUUID().toString().replace("-", ""));
        params.put("data", JSON.toJSONString(data));
        // 生成签名
        String sign = SignUtil.generateSign(params, secret);
        params.put("sign", sign);
        return params;
    }
}

最佳实践建议

  1. 使用HTTPS:确保传输层安全
  2. 密钥管理:密钥不要硬编码,使用配置中心
  3. 时间戳验证:防止重放攻击
  4. 参数排序:保证签名一致性
  5. 包含敏感数据:签名应包含所有参数
  6. 错误处理:提供清晰的错误信息

这就是Java接口签名的完整实现方案,可以根据实际需求选择合适的实现方式。

抱歉,评论功能暂时关闭!