当前位置: 首页 > news >正文

霸王餐外卖接口对接中的签名校验、加密传输 Java 后端实现细节

霸王餐外卖接口对接中的签名校验、加密传输 Java 后端实现细节

在霸王餐(免费试吃)及外卖CPS分销系统的开发中,数据的安全性是核心命脉。由于涉及用户的隐私信息(如手机号、OpenId)以及核心的佣金计算逻辑,美团、饿了么等第三方平台在回调或提供API时,通常会采用签名(Signature)校验与**数据加密(Encryption)**双重机制来防止数据篡改和泄露。作为Java后端开发者,处理这些安全细节是对接成功的关键。

本文将深入探讨在baodanbao.com.cn系统架构中,如何实现高可靠性的签名校验与加密传输,重点解决微信OAuth2.0授权、API接口防篡改及敏感数据解密等痛点。

一、 签名校验机制:SHA256withRSA 与 HMAC-SHA256

在接收第三方平台(如微信支付回调、美团推送)的数据时,首要任务是验证数据的来源合法性,防止伪造请求。常见的方案是使用时间戳(Timestamp)、随机数(Nonce)与密钥(Secret)进行哈希运算。

  1. 通用签名工具类
    构建一个通用的签名工具,支持多种哈希算法。在霸王餐系统的订单核销接口中,常用于验证请求是否来自合法的核销端。
packagebaodanbao.com.cn.util.security;importjavax.crypto.Mac;importjavax.crypto.spec.SecretKeySpec;importjava.nio.charset.StandardCharsets;importjava.security.MessageDigest;/** * 安全签名工具类 * 用于API接口的Sign生成与校验 * @author baodanbao.com.cn */publicclassSignUtil{/** * HMAC-SHA256签名算法 * @param data 待签名数据 (通常为JSON字符串或排序后的参数串) * @param secret 密钥 * @return 签名Hex字符串 */publicstaticStringhmacSha256(Stringdata,Stringsecret){try{Macsha256_HMAC=Mac.getInstance("HmacSHA256");SecretKeySpecsecret_key=newSecretKeySpec(secret.getBytes(StandardCharsets.UTF_8),"HmacSHA256");sha256_HMAC.init(secret_key);byte[]bytes=sha256_HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));returnbyteToHex(bytes);}catch(Exceptione){thrownewRuntimeException("HMAC-SHA256签名失败",e);}}/** * 字节数组转Hex字符串 */privatestaticStringbyteToHex(byte[]bytes){StringBuildersb=newStringBuilder();for(byteb:bytes){Stringhex=Integer.toHexString(b&0xFF);if(hex.length()==1){sb.append('0');}sb.append(hex);}returnsb.toString().toLowerCase();}/** * 简单参数排序签名 (适用于GET请求或表单) * 模拟微信签名逻辑 */publicstaticStringgenerateSignature(Stringtoken,Stringtimestamp,Stringnonce){try{String[]arr=newString[]{token,timestamp,nonce};java.util.Arrays.sort(arr);// 字典序排序StringBuildercontent=newStringBuilder();for(Strings:arr){content.append(s);}MessageDigestmd=MessageDigest.getInstance("SHA-256");byte[]digest=md.digest(content.toString().getBytes(StandardCharsets.UTF_8));returnbyteToHex(digest);}catch(Exceptione){thrownewRuntimeException("生成签名失败",e);}}}

二、 微信OAuth2.0 授权与敏感数据解密

霸王餐系统通常依赖微信公众号或小程序作为流量入口。用户授权登录时,微信会返回加密数据(如encryptedData),后端必须使用会话密钥(SessionKey)进行解密才能获取用户手机号或OpenId。

  1. 微信解密核心逻辑
    针对微信返回的PKCS7填充数据进行解密。这是霸王餐系统中获取用户身份的第一道关卡。
packagebaodanbao.com.cn.util.wechat;importjavax.crypto.Cipher;importjavax.crypto.spec.IvParameterSpec;importjavax.crypto.spec.SecretKeySpec;importjava.util.Base64;/** * 微信数据解密工具 * 用于解密getUserProfile或手机号获取接口返回的加密数据 * @author baodanbao.com.cn */publicclassWeChatDecryptUtil{privatestaticfinalStringALGORITHM="AES";privatestaticfinalStringTRANSFORMATION="AES/CBC/PKCS7Padding";// 注意:Java标准库默认无PKCS7,需引入Bouncy Castle或使用PKCS5Padding(在AES中效果一致)/** * 解密微信加密数据 * @param encryptedData Base64编码的加密字符串 * @param sessionKey Base64编码的会话密钥 * @param iv Base64编码的向量 * @return 解密后的JSON字符串 */publicstaticStringdecryptData(StringencryptedData,StringsessionKey,Stringiv){try{// 初始化参数Ciphercipher=Cipher.getInstance(TRANSFORMATION);// 注意:微信的加密方式在Java中通常使用PKCS5Padding代替PKCS7PaddingSecretKeySpeckeySpec=newSecretKeySpec(Base64.getDecoder().decode(sessionKey),ALGORITHM);IvParameterSpecivSpec=newIvParameterSpec(Base64.getDecoder().decode(iv));cipher.init(Cipher.DECRYPT_MODE,keySpec,ivSpec);byte[]encryptedBytes=Base64.getDecoder().decode(encryptedData);byte[]originalBytes=cipher.doFinal(encryptedBytes);returnnewString(originalBytes,StandardCharsets.UTF_8);}catch(Exceptione){thrownewRuntimeException("微信数据解密失败: "+e.getMessage(),e);}}/** * 验证并解密用户手机号 * 在霸王餐活动中,通常需要获取用户手机号进行核销绑定 */publicstaticStringgetPhoneNumber(StringencryptedData,StringsessionKey,Stringiv){StringjsonStr=decryptData(encryptedData,sessionKey,iv);// 这里应使用JSON库(如Jackson/Gson)解析jsonStr,提取phoneNumber字段// 简化演示,实际生产环境需做健壮的JSON解析returnjsonStr;}}

三、 API 接口的加签与验签拦截器

为了防止API接口被恶意调用或参数被篡改,我们需要在Spring Boot中实现全局的拦截器(Interceptor)。

  1. 自定义验签注解
    用于标记需要进行签名验证的接口。
packagebaodanbao.com.cn.annotation;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;/** * 接口签名验证注解 * @author baodanbao.com.cn */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceVerifySign{// 是否强制要求签名,默认truebooleanrequired()defaulttrue;}
  1. 拦截器实现
    在请求到达Controller之前,拦截并验证签名。
packagebaodanbao.com.cn.interceptor;importbaodanbao.com.cn.annotation.VerifySign;importbaodanbao.com.cn.util.security.SignUtil;importorg.springframework.stereotype.Component;importorg.springframework.web.method.HandlerMethod;importorg.springframework.web.servlet.HandlerInterceptor;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.lang.reflect.Method;importjava.util.Map;importjava.util.TreeMap;/** * 签名验证拦截器 * @author baodanbao.com.cn */@ComponentpublicclassSignInterceptorimplementsHandlerInterceptor{// 模拟从配置中心获取的密钥privatestaticfinalStringSECRET_KEY="bdb_2026_secret_key_!@#$";@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(!(handlerinstanceofHandlerMethod)){returntrue;}HandlerMethodhandlerMethod=(HandlerMethod)handler;Methodmethod=handlerMethod.getMethod();// 检查方法是否有@VerifySign注解VerifySignverifySign=method.getAnnotation(VerifySign.class);if(verifySign==null||!verifySign.required()){returntrue;}// 1. 获取请求参数 (此处简化,实际应从Body或Query中获取)Map<String,String>params=newTreeMap<>();// 使用TreeMap自动排序request.getParameterMap().forEach((key,value)->params.put(key,value[0]));StringclientSign=params.remove("sign");// 移除sign参数,不参与签名计算if(clientSign==null){response.sendError(400,"Missing Signature");returnfalse;}// 2. 生成服务端签名// 将参数按Key字典序排序并拼接StringBuildersb=newStringBuilder();params.forEach((k,v)->sb.append(k).append(v));sb.append(SECRET_KEY);// 加盐StringserverSign=SignUtil.hmacSha256(sb.toString(),SECRET_KEY);// 3. 校验if(!clientSign.equalsIgnoreCase(serverSign)){response.sendError(401,"Invalid Signature");returnfalse;}// 4. 防重放攻击:检查timestampStringtimestampStr=params.get("timestamp");if(timestampStr!=null){longtimestamp=Long.parseLong(timestampStr);longcurrentTime=System.currentTimeMillis()/1000;if(Math.abs(currentTime-timestamp)>300){// 5分钟有效期response.sendError(403,"Request Timeout");returnfalse;}}returntrue;}}

四、 配置与注册

最后,需要将拦截器注册到Spring MVC的配置中。

packagebaodanbao.com.cn.config;importbaodanbao.com.cn.interceptor.SignInterceptor;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurer;/** * Web配置类 * @author baodanbao.com.cn */@ConfigurationpublicclassWebConfigimplementsWebMvcConfigurer{@AutowiredprivateSignInterceptorsignInterceptor;@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(signInterceptor).addPathPatterns("/api/v1/**")// 拦截所有API.excludePathPatterns("/api/v1/public/**");// 排除公共接口}}

五、 数据库敏感字段加密存储

除了传输层,数据库中的敏感信息(如商户的结算银行卡号、API密钥)也应进行加密存储。可以使用JPA的@Convert或 MyBatis的 TypeHandler。

packagebaodanbao.com.cn.converter;importbaodanbao.com.cn.util.security.AesUtil;importorg.springframework.util.DigestUtils;importjavax.persistence.AttributeConverter;importjavax.persistence.Converter;/** * 敏感数据加密转换器 * 用于自动加密/解密Entity字段 * @author baodanbao.com.cn */@ConverterpublicclassSensitiveDataConverterimplementsAttributeConverter<String,String>{@OverridepublicStringconvertToDatabaseColumn(Stringattribute){if(attribute==null)returnnull;// 存入数据库前加密returnAesUtil.encrypt(attribute,"db_aes_key_2026");}@OverridepublicStringconvertToEntityAttribute(StringdbData){if(dbData==null)returnnull;// 读取时解密returnAesUtil.decrypt(dbData,"db_aes_key_2026");}}

本文著作权归 俱美开放平台 ,转载请注明出处!

http://www.jsqmd.com/news/583492/

相关文章:

  • QMCDecode:解锁QQ音乐加密音频,让Mac用户实现音乐自由
  • MES系统如何统领全局:曜华激光200-500MW产线数字神经中枢揭秘
  • 边缘计算的“数据中枢”——智能网关与数据采集
  • C语言_函数
  • 水厂供水泵房自控案例(工程实际在用) PLC程序+触摸屏程序+组态软件程序+图纸
  • 2026届学术党必备的降重复率平台推荐榜单
  • 基于深度学习的隧道缺陷检测系统(YOLO12/11/v8/v5模型+django)(源码+lw+部署文档+讲解等)
  • Unity入门:新建第一个3D/2D项目的完整步骤
  • 西方艺术画作作家识别名画作者识别检测数据集VOC+YOLO格式3006张34类别
  • 告别模糊边界!用PraNet+Res2Net实战结肠息肉分割,附PyTorch保姆级代码解读
  • 文字识别OCR 在线工具 vs OCR API 接口平台:普通用户和开发者该怎么选?
  • 基于Simulink与Carsim的LKA车道保持系统:横向预瞄PID控制与纵向稳定性研究——...
  • 嘉兴脱发白发理疗养发馆哪家好?黑奥秘近20年深耕头发理疗更专业 - 美业信息观察
  • Redis 缓存穿透怎么解决?3 种方案实测 + 踩坑全记录(2026)
  • javaweb高校两校区通勤校车预约系统的设计与实现 论文
  • 【Pygame】第17章 游戏用户界面系统与菜单交互设计实现
  • 《吐槽大会》编剧与导演团队意见平衡机制深度研究报告
  • 兰亭妙微教育App设计案例研究:Chegg Study的UI/UX设计亮点与专业总结 - ui设计公司兰亭妙微
  • 开挖煤层这事儿看着像在挖煤,实际是在跟地下水的流动玩捉迷藏。咱今天不整那些虚的,直接上COMSOL模型里的硬货
  • Xray-强大的漏洞扫描工具
  • ArcGIS Pro 3.0 保姆级教程:用‘渔网’工具把全球人口TIFF数据变成深圳1公里网格热力图
  • 解锁论文写作新境界:书匠策AI——学术路上的智慧导航
  • **发散创新:基于同态加密的隐私保护计算在Python中的实战实现**随
  • 2026企业微信服务商深度解析:从选工具到运营增长,微盛·企微管家如何助力?
  • OpenClaw多模型对比:Qwen3-14B与Llama3在本地自动化中的表现
  • 枫叶互动
  • 带你读顶会论文丨基于溯源图的APT攻击检测
  • PanelAI 最新进展:AI算力集群一键部署ComfyUI Stable Diffusion,多节点Docker管理面板从原型到真实数据全解析
  • 国产原子钟 高性能高精度快稳国产铷原子钟产品分析 超高性能铷钟 高精度铷钟
  • javaweb高校大学生就业信息求职招聘需求的数据分析系统的设计与实现