推送鉴权

云客服对外推送事件支持鉴权功能,客户需要按照云客服鉴权规则进行鉴权,避免出现假请求、模拟请求带来的脏数据

1、 云客服推送鉴权

云客服将使用设置-对接中心-鉴权信息中的appId以及密钥进行请求头的生成,具体生成规则可参考 接口鉴权说明

2、 客户鉴权解析示例

客户在接受到推送后,可根据请求头中的参数进行鉴权对应,保证请求来源正常后再进行业务数据入库
请求demo:
package com.ue.sendout.utils; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import javax.servlet.http.HttpServletRequest; import java.util.Base64; import java.util.HashMap; import java.util.Map; /** * Useasy事件推送签名验证Demo * * 用途:验证来自Useasy系统的请求是否合法 * 原理:通过HMAC-SHA256签名算法验证请求的真实性 */ @RestController @RequestMapping(value = "/signature") public class UseasySignatureValidator { /** * 验证Useasy推送请求的签名 * * @param appId 应用ID(从请求头appid获取) * @param secretKey 密钥(在Useasy系统中配置的密钥) * @param nonce 随机数(从请求头nonce获取) * @param timestamp 时间戳(从请求头timestamp获取) * @param signature 签名(从请求头signature获取) * @return true=验证通过,false=验证失败 */ public static boolean verifySignature(String appId, String secretKey, String nonce, String timestamp, String signature) { try { // 第一步:构造签名消息 // 规则:appId + timestamp + nonce String message = appId + timestamp + nonce; // 第二步:使用HMAC-SHA256算法加密 Mac mac = Mac.getInstance("HmacSHA256"); SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256"); mac.init(keySpec); byte[] hmacBytes = mac.doFinal(message.getBytes()); // 第三步:Base64编码得到期望的签名 String expectedSignature = Base64.getEncoder().encodeToString(hmacBytes); // 第四步:比较签名 return expectedSignature.equals(signature); } catch (Exception e) { System.err.println("签名验证失败: " + e.getMessage()); return false; } } /** * 事件推送接收接口示例 */ @PostMapping("/your-webhook-url") public Map<String, Object> receiveEvent(@RequestBody String body, HttpServletRequest request) { // 获取请求头中的鉴权信息 String appId = request.getHeader("appid"); String nonce = request.getHeader("nonce"); String timestamp = request.getHeader("timestamp"); String signature = request.getHeader("signature"); // 1.基础参数校验 if (appId == null || nonce == null || timestamp == null || signature == null) { System.out.println("参数校验失败:缺少必要的鉴权参数"); return buildResponse("400", "缺少必要的鉴权参数"); } // 2.时间戳校验 try { long ts = Long.parseLong(timestamp); long currentTime = System.currentTimeMillis() / 1000; long timeDiff = Math.abs(currentTime - ts); // 检查时间戳是否在5分钟内 if (timeDiff > 300) { return buildResponse("401", "请求时间戳已过期"); } } catch (NumberFormatException e) { return buildResponse("400", "时间戳格式错误"); } // 您的密钥(需要与Useasy系统中配置的保持一致) String secretKey = "your_secret_key"; // 您的账户ID(用于验证appId) String expectedAppId = "your_account_id"; // 3.appId校验 if (!expectedAppId.equals(appId)) { System.out.println("appId校验失败:不匹配"); return buildResponse("401", "appId验证失败"); } // 4.签名验证 boolean isValid = verifySignature(appId, secretKey, nonce, timestamp, signature); if (isValid) { // 签名验证通过,处理业务逻辑 System.out.println("所有校验通过,请求来自Useasy系统"); // TODO: 处理具体的业务逻辑 return buildResponse("200", "事件处理成功"); } else { // 签名验证失败,拒绝请求 System.out.println("签名验证失败,拒绝请求"); return buildResponse("401", "签名验证失败"); } } /** * 构建标准响应格式 */ private Map<String, Object> buildResponse(String code, String message) { Map<String, Object> response = new HashMap<>(); response.put("code", code); response.put("message", message); response.put("timestamp", System.currentTimeMillis() / 1000); return response; } }
2025-07-25