导读:本期聚焦于白鲨创作的《Spring Boot后端如何整合声网Agora SDK生成Token并支撑音视频通话?》,敬请观看详情。为什么客户端一进频道就报 101 错误?多数情况是后端生成的声网 Token 不合法或者已经过期。Spring Boot 在音视频通话方案里的核心工作不是转发媒体流,而是安全地生成限时 Token、维护频道用户关系并接收声网回调。声网采用 App ID 加 App Certificate 的双凭证鉴权,服务端引入官方 Java 工具包后,可以用 RtcTokenBuilder2 按频道名、uid、角色生成带有过期时间的动态密钥。本文会从依赖配置、Token 封装、REST 接口设计、回调验签和常见错误排查几个方面完整走通后端流程。读完你可以直接搭建一个支持一对一或多人音视频通话的 Spring Boot 后端服务。

如果客户端只拿到声网 App ID 而没有合法 Token,进入频道时会直接收到 101 错误。音视频通话的后端并不负责媒体流转发,真正要解决的是身份鉴权、频道管理和回调处理。Spring Boot 项目通过引入 Agora 官方 Java Token 工具包,可以在服务端安全生成限时 Token,再把 Token 下发给客户端,整个流程会更可控。

Spring Boot后端如何整合声网Agora SDK生成Token并支撑音视频通话?

声网的媒体数据走自己的 SFU 网络分发,业务服务器只需要和声网云做两件关键交互:签发动态密钥,以及接收频道事件通知。下面从鉴权机制开始,逐层把后端实现拆开。

一、后端在声网音视频通话中的职责边界

很多初学者会把声网 SDK 当成一个纯粹的前端库,认为只要在前端调用 joinChannel 就能完成通话。实际上,声网要求加入频道时必须携带 Token,而 Token 的生成依赖 App Certificate 这个私密凭证。它一旦写进前端代码或打包进客户端,任何人都可以反编译拿到,然后伪造任意频道的 Token。因此 Token 必须由后端生成,再通过鉴权接口下发给登录用户。

除了签发 Token,后端还要承担几个轻量但重要的工作。第一是维护频道和用户的映射关系,避免两个终端使用相同 uid 互相抢占;第二是记录业务侧的房间状态,便于统计和排查;第三是接收声网的消息通知,例如用户进出频道、录制结束等事件。媒体流本身不会经过 Spring Boot 服务,所以即使房间人数很多,后端的压力也不会随着音视频码率线性增长。

声网 Token 本质上是一个带有过期时间的动态密钥,它会绑定 App ID、频道名、uid 和角色。App ID 用来标识项目,App Certificate 用来签名,channelName 表示房间,uid 表示用户,role 决定是发流还是只订阅。过期时间可以精确到秒,服务端可以根据业务场景设置较短的有效期,降低泄露风险。

二、引入 Agora Java 工具包并封装 Token 服务

声网官方提供了 Java 版本的 Token 生成工具包,Maven 坐标归属 io.agora,不需要自己实现 HMAC 签名算法。引入依赖后,核心类 RtcTokenBuilder2 已经封装了 App ID、App Certificate 和参数拼接逻辑。为了配置可维护,通常把凭证放在 application.yml 中,通过 @Value 注入。

<dependency>
    <groupId>io.agora</groupId>
    <artifactId>authentication</artifactId>
    <version>2.1.0</version>
</dependency>

依赖版本建议以 Maven Central 上的最新版本为准,2.x 系列已经同时支持 RtcTokenBuilder2 和 AccessToken2。使用 RtcTokenBuilder2 构建 Token 时,需要区分 tokenExpire 和 privilegeExpire。tokenExpire 表示整个 Token 的有效期,privilegeExpire 表示某一项权限的持续时间。对于常规音视频通话,两者可以设置相同值,通常不超过 7200 秒。

下面是一个可直接复用的服务类,把 uid 固定为整型并允许外部指定角色。发布者角色可以发流和收流,订阅者角色只能收流,这在直播场景里非常实用。

import io.agora.media.RtcTokenBuilder2;
import io.agora.media.RtcTokenBuilder2.Role;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class AgoraTokenService {

    private final String appId;
    private final String appCertificate;
    private final int expireSeconds;

    public AgoraTokenService(@Value("${agora.app-id}") String appId,
                             @Value("${agora.app-certificate}") String appCertificate,
                             @Value("${agora.token-expire-seconds:3600}") int expireSeconds) {
        this.appId = appId;
        this.appCertificate = appCertificate;
        this.expireSeconds = expireSeconds;
    }

    public String buildRtcToken(String channelName, int uid, boolean publisher) {
        RtcTokenBuilder2 builder = new RtcTokenBuilder2();
        Role role = publisher ? Role.ROLE_PUBLISHER : Role.ROLE_SUBSCRIBER;
        return builder.buildTokenWithUid(
                appId,
                appCertificate,
                channelName,
                uid,
                role,
                expireSeconds,
                expireSeconds
        );
    }
}

这段代码没有打印 App Certificate,也没有把它暴露给外部。实际部署时,application.yml 中的证书应通过环境变量、配置中心或密钥管理服务注入,避免提交到 Git 仓库。对 Token 做缓存没有太大意义,因为它的价值就在于短期有效,每次请求用原始凭证重新签发即可。

三、设计 REST 接口并校验请求参数

客户端需要的是一个简单明确的 HTTP 接口,比如 POST /api/agora/token,提交频道名、uid 和角色,拿到 Token。为了不让客户端随意指定 uid,通常可以先根据用户 ID 从数据库或缓存中获取映射后的整型 uid,也可以让客户端传一个业务用户标识,后端再转换成声网 uid。这里为了演示,直接接收 uid,但会在生产代码中建议改成后端分配。

参数校验不能忽略。频道名长度、uid 范围、角色类型都需要限制,否则容易造成无效请求。Spring Boot 可以配合 spring-boot-starter-validation,在 DTO 上使用注解约束。

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;

public class TokenRequest {

    @NotBlank(message = "频道名不能为空")
    private String channelName;

    @Min(value = 0, message = "uid 不能为负数")
    private int uid;

    private boolean publisher = true;

    public String getChannelName() {
        return channelName;
    }

    public void setChannelName(String channelName) {
        this.channelName = channelName;
    }

    public int getUid() {
        return uid;
    }

    public void setUid(int uid) {
        this.uid = uid;
    }

    public boolean isPublisher() {
        return publisher;
    }

    public void setPublisher(boolean publisher) {
        this.publisher = publisher;
    }
}

Controller 层保持轻薄,只负责参数绑定和调用服务。返回结构可以使用 Map,也可以定义统一的响应对象。这里为了直接可用,使用 Map 返回 Token、频道名和 uid。

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/agora")
public class AgoraController {

    private final AgoraTokenService tokenService;

    public AgoraController(AgoraTokenService tokenService) {
        this.tokenService = tokenService;
    }

    @PostMapping("/token")
    public ResponseEntity<Map<String, String>> getToken(@RequestBody @Valid TokenRequest request) {
        String channelName = request.getChannelName();
        int uid = request.getUid();
        boolean publisher = request.isPublisher();

        String token = tokenService.buildRtcToken(channelName, uid, publisher);

        Map<String, String> result = new HashMap<>();
        result.put("token", token);
        result.put("channelName", channelName);
        result.put("uid", String.valueOf(uid));
        result.put("role", publisher ? "publisher" : "subscriber");
        return ResponseEntity.ok(result);
    }
}

这个接口已经可以支撑基础的一对一和多人通话。对于直播场景,主播请求时传 publisher 为 true,观众传 false,就能从权限层面阻止观众推流。更复杂的业务还可以在服务端维护房间成员表,在用户加入前判断是否达到人数上限,或是否需要房间密码。

四、处理声网消息通知与回调验签

声网的消息通知服务可以把频道事件推送到你的后端,典型事件包括用户加入、用户离开、录制状态变化等。配置回调地址后,声网会以 HTTP POST 形式请求你的服务器,并在请求头中携带签名。如果不验证签名,任何人都可以伪造回调,扰乱业务统计甚至触发错误的房间释放逻辑。

验签的基本思路是把回调请求体与约定的密钥拼在一起做摘要,然后和请求头中的签名比对。声网在不同产品线的具体算法略有差异,有的使用 MD5,有的使用 HMAC-SHA1,接入时要以控制台显示的算法为准。下面演示一种 MD5 验签流程,帮助理解整体结构。

import org.springframework.http.ResponseEntity;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import java.nio.charset.StandardCharsets;

@RestController
@RequestMapping("/api/agora")
public class AgoraCallbackController {

    private final String callbackKey = "your-callback-key";

    @PostMapping("/callback")
    public ResponseEntity<String> handleCallback(@RequestBody String body,
                                                   @RequestHeader("Agora-Signature") String signature) {
        String expected = DigestUtils.md5DigestAsHex(
                (callbackKey + body).getBytes(StandardCharsets.UTF_8)
        );
        if (!expected.equalsIgnoreCase(signature)) {
            return ResponseEntity.status(403).body("invalid signature");
        }

        // 验签通过后,异步处理事件,避免阻塞声网重试
        processEventAsync(body);
        return ResponseEntity.ok("success");
    }

    private void processEventAsync(String body) {
        // 将事件投递到消息队列或线程池,按业务需要处理
    }
}

回调接口要尽快返回成功响应。声网在请求超时或收到非 2xx 状态码时可能进行重试,因此不要在回调里做长时间同步处理。可以先把原始事件写入 MQ 或数据库,再由后台任务慢慢消费。业务侧需要根据 eventType 区分处理,例如用户离开后更新房间成员缓存,录制完成后转码并生成回放记录。

安全方面,Token 的有效期不建议设置超过 7200 秒,移动端弱网重连如果发现 Token 过期,应重新向后端申请。后端接口必须启用 HTTPS,防止 Token 在传输过程中被窃听。对于回调地址,建议只允许声网的出口 IP 访问,或至少保证验签正确后再执行业务逻辑。

五、本地联调与常见错误排查

后端接口开发完成后,可以先用 Postman 或 curl 请求 /api/agora/token,确认返回的 Token 是长度为三位数左右的字符串,并且不包含换行。然后打开声网官方示例客户端,填入相同的 App ID、频道名、uid 和后端返回的 Token。如果客户端控制台出现 101 错误,基本可以断定 Token 校验失败,需要检查 App Certificate 是否复制完整,前后有没有多余空格。

另一个高频问题是 uid 类型不一致。声网支持字符串 uid 和整型 uid,如果你的 Token 按整型 uid 生成,但客户端加入频道时传了字符串 uid,也可能导致鉴权失败。统一在接口文档中规定 uid 类型,并在后端固定生成规则,能避免大部分奇怪问题。

Token 过期也是常见现象。联调时可以把过期时间临时调长,但上线前要恢复为 3600 秒左右。客户端应在收到声网 onTokenPrivilegeWillExpire 回调时提前向后端刷新 Token,而不是等 Token 完全失效后再处理。后端日志里不要输出完整 Token 和证书,避免泄露到日志系统。

Spring Boot声网SDK音视频通话修改时间:2026-09-29 21:39:10

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0929/63564.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。