Java/Spring接收Viber REST API回调问题排查与求助
解决Viber公共账号机器人在Spring中接收用户消息的问题
看起来你已经迈出了不错的第一步——成功部署到Heroku并通过Postman设置了Webhook,但用户消息没触发日志,核心问题在于当前的控制器没有正确匹配Viber的回调请求格式,也没处理它发送的事件 payload。我来帮你理清Viber的回调机制,并给出可运行的修正方案。
先搞懂Viber的回调逻辑
Viber的机器人回调是POST请求:当用户发送消息、关注机器人或产生其他交互事件时,Viber服务器会向你设置的Webhook URL发送两个关键内容:
X-Viber-Content-Signature请求头(用来验证请求是否来自合法的Viber服务器)- JSON格式的请求体,包含事件类型、用户信息、消息内容等核心数据
你的当前代码只处理了请求头,既没指定POST方法,也没接收请求体,而且@RequestMapping默认匹配所有路径和方法,可能和其他路由冲突,这就是用户消息没触发日志的直接原因。
分步解决问题
1. 创建Viber事件的实体类
首先需要定义对应Viber回调JSON的Java实体,让Spring能自动解析请求体。这里给出包含核心字段的示例:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @JsonIgnoreProperties(ignoreUnknown = true) public class ViberEvent { @JsonProperty("event") private String eventType; // 比如 "message", "subscribed" 等事件类型 @JsonProperty("sender") private ViberUser sender; @JsonProperty("message") private ViberMessage message; // getter和setter方法 public String getEventType() { return eventType; } public void setEventType(String eventType) { this.eventType = eventType; } public ViberUser getSender() { return sender; } public void setSender(ViberUser sender) { this.sender = sender; } public ViberMessage getMessage() { return message; } public void setMessage(ViberMessage message) { this.message = message; } @JsonIgnoreProperties(ignoreUnknown = true) public static class ViberUser { @JsonProperty("id") private String userId; @JsonProperty("name") private String userName; // getter和setter方法 public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } } @JsonIgnoreProperties(ignoreUnknown = true) public static class ViberMessage { @JsonProperty("text") private String text; @JsonProperty("type") private String messageType; // 比如 "text" // getter和setter方法 public String getText() { return text; } public void setText(String text) { this.text = text; } public String getMessageType() { return messageType; } public void setMessageType(String messageType) { this.messageType = messageType; } } }
2. 修正Spring控制器
更新你的控制器,指定POST方法、匹配Webhook的路径,并同时接收签名头和请求体:
import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RestController; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @RestController public class DemoApplication { // 从环境变量读取Token,不要硬编码!Heroku可通过配置变量设置 private static final String VIBER_AUTH_TOKEN = System.getenv("VIBER_AUTH_TOKEN"); public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } // 路径要和你设置Webhook时的URL完全一致,比如你设的是https://xxx.herokuapp.com/viber/webhook,就写"/viber/webhook" @PostMapping("/viber/webhook") public String handleViberCallback( @RequestHeader("X-Viber-Content-Signature") String serverSideSignature, @RequestBody String requestBody) throws Exception { // 先验证签名合法性 if (!ViberSignatureValidator.isValidSignature(requestBody, serverSideSignature, VIBER_AUTH_TOKEN)) { System.out.println("收到无效的Viber请求:签名验证失败"); return "Invalid Signature"; } // 解析请求体为ViberEvent对象 ObjectMapper objectMapper = new ObjectMapper(); ViberEvent viberEvent = objectMapper.readValue(requestBody, ViberEvent.class); // 打印事件日志 System.out.println("===== 收到Viber事件 ====="); System.out.println("事件类型: " + viberEvent.getEventType()); System.out.println("发送用户ID: " + viberEvent.getSender().getUserId()); System.out.println("发送用户昵称: " + viberEvent.getSender().getUserName()); if ("message".equals(viberEvent.getEventType())) { System.out.println("消息内容: " + viberEvent.getMessage().getText()); } // 必须返回200 OK的响应,Viber才会认为回调成功 return "OK"; } }
3. 实现签名验证(关键步骤)
Viber要求你验证请求签名,确保请求来自官方服务器。签名是用机器人的Authentication Token和请求体做HMAC-SHA256加密后生成的Base64字符串,工具方法如下:
import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; public class ViberSignatureValidator { private static final String HMAC_SHA256 = "HmacSHA256"; public static boolean isValidSignature(String requestBody, String signatureHeader, String authToken) { try { SecretKeySpec secretKey = new SecretKeySpec(authToken.getBytes(), HMAC_SHA256); Mac mac = Mac.getInstance(HMAC_SHA256); mac.init(secretKey); byte[] hashBytes = mac.doFinal(requestBody.getBytes()); String computedSignature = Base64.getEncoder().encodeToString(hashBytes); return computedSignature.equals(signatureHeader); } catch (Exception e) { e.printStackTrace(); return false; } } }
4. 确认Webhook设置正确
- 确保设置Webhook时的URL和控制器
@PostMapping的路径完全一致(比如https://your-heroku-app-name.herokuapp.com/viber/webhook) - 重新设置Webhook:用Postman发送POST请求到
https://chatapi.viber.com/pa/set_webhook,请求体带上你的Auth Token和正确的Webhook URL - 用
heroku logs --tail命令查看Heroku实时日志,观察用户发送消息时是否有请求到达
调试小技巧
- 用Heroku实时日志功能(
heroku logs --tail)追踪请求状态 - 如果仍无请求,检查Viber开发者后台的Webhook状态,查看是否有错误提示
- 确认Heroku应用处于运行状态(用
heroku ps命令查看)
内容的提问来源于stack exchange,提问作者Mykhailo Bormashenko
相关产品推荐
相关产品推荐

