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