视频聊天应用程序的安全至关重要。声网平台的安全防护之一是令牌(Token)认证。本指南的主要内容是使用 Java 和 Jersey 构建简单的微服务以生成声网 RTE 令牌。
前期准备
- Apache Tomcat、IntelliJ IDEA IDE、Jersey 框架和 Docker;
- 对 REST 框架工作原理有基本了解;
- 对 Java 类有基本了解;
- 有声网开发者帐户(详细步骤可参考这篇文章)。
主要事项
- 构建一个 Jersey 项目;
- 创建一个 Hello World API;
- 生成声网令牌;
- 生成 Dockerize 应用程序并将其部署在 Ubuntu VM 上。
创建项目
Archetype:Archetype 是一个 Maven 项目模板工具包。
Artifact:Artifact 是一个文件,通常是一个部署在 Maven 资源库的 JAR 文件。构建好的 Maven 能产生一个或多个 artifacts,比如,已编译的 JAR 文件和 JAR 源文件。
jersey-quickstart-webapp 提供基本的项目模板,对初学者非常有用。
打开 IntelliJ IDE ,然后单击“开始新项目”,我们用 Maven 创建新项目。单击 Archetype 中的创建,添加以下配置的 Archetype。
Group Id: org.glassfish.jersey.archetypes
Artifact Id: jersey-quickstart-webapp
Version: 2.31
项目的最终配置如下:
项目创建完成后,添加 Tomcat 服务器,大家可以从 Tomcat 官方网站获取 Tomcat。Tomcat 安装完成后,单击创建图标旁边的添加配置。
在模板中选择本地 Tomcat 服务器,最终配置如下:
完成后应用该配置并关闭配置窗口。现在,我们已经完成了 Jersey 项目的创建并配置了 Tomcat 服务器。
Hello World API
本节我们将用 JSON 和 XML 的输入和输出创建 Hello World API。
找到 pom.xml(它有所有 Maven 依赖项),取消注释 JSON 依赖项,然后添加 JAVAX 依赖项。你的依赖项应如下:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet-core</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
编辑 src/main/webapp/index.JSP 并将下列代码添加到文件中:
<html>
<body>
<h2>Agora Token Server</h2>
<p><a href="webapi/myresource">Agora resource</a>
<p>Visit <a href="https://agora.io/">Agora.io website</a>
for more information on Agora!
</body>
</html>
接下来,构建项目。构建完成后,用 IntelliJ 打开浏览器。默认情况下,index.JSP 文件显示在浏览器中。浏览器应显示以下内容:
单击 jersey 资源,跳转到新页面:
请注意 URL,默认情况下,ServerletMapping 将所有内容映射到 */webapi/ 上,你要将其改为/*。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>Agora Token Server</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.sandeep.agoratoken</param-value> <!-- Remember to replace this -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Agora Token Server</servlet-name>
<url-pattern>/webapi/*</url-pattern>
</servlet-mapping>
</web-app>
删除 webapi 后,重新启动服务器。
怎么让“Got it”显示在网站上?转到 MyResource Java 类:
这里我们添加了路径注释(@PATH),用于为 MyResource 类传入的 HTTP 请求定义一个 URI 匹配模式。
因为 Path annotation ,我们从 URL 中获得“myresource”,即@ PATH(“myresource”)。我们为 getIt() 方法定义了 @ GET (第 12 行),getIt() 方法指定使用 GET HTTP 方法。@ Produces annotation(第 13 行) 定义返回响应格式。
现在,将声网添加到项目中。
访问声网 GitHub 仓库,克隆整个仓库,复制并粘贴 Media 和 RTM 文件夹,文件结构应如下:
注意:找到每个文件并更正软件包名称,避免出现错误。本项目的软件包名称应该是 com.sandeep.agoratoken,不是 io.agora。通常 IntelliJ 会执行此操作。
分别在 Agora、AgoraRepository 和 AgoraRTMRepository 这三个 java 文件夹上单击右键,删除 MyResource 文件后,3个新的 Java 类就创建完成了。
将以下代码粘贴到 AgoraRepository 中:
package com.sandeep.agoratoken; //make sure you change this
public class AgoraRepository {
static String appId = "APP_ID";
static String appCertificate ="APP_CERIFICATE";
private String channelName;
private int uid = 0; // By default 0
private int expirationTimeInSeconds = 3600; // By default 3600
private int role = 2; // By default subscriber
}
将以下代码粘贴到 AgoraRTMRepository 中:
package com.sandeep.agoratoken;
public class AgoraRTMRepository {
private static String appId = "APP_ID";
private static String appCertificate = "APP_CERT";
private String userId;
private int expireTimestamp = 0;
}
现在,你需要获取器和设置器。
转到 AgoraRepository 类,在操作系统输入下列命令:
* Ctrl + N for Linux and Windows users
* Cmd + N for Mac users
上述命令的替换
你的文件应如下:
package com.sandeep.agoratoken;
public class AgoraRepository {
static String appId = "APP_ID"; //replace app id
static String appCertificate = "APP_CERT"; //replace app cert
private String channelName;
private int uid = 0;
private int expirationTimeInSeconds = 3600;
private int role = 2; // By default subscriber
public static String getAppId() {
return appId;
}
public static void setAppId(String appId) {
AgoraRepository.appId = appId;
}
public static String getAppCertificate() {
return appCertificate;
}
public static void setAppCertificate(String appCertificate) {
AgoraRepository.appCertificate = appCertificate;
}
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 int getExpirationTimeInSeconds() {
return expirationTimeInSeconds;
}
public void setExpirationTimeInSeconds(int expirationTimeInSeconds) {
this.expirationTimeInSeconds = expirationTimeInSeconds;
}
public int getRole() {
return role;
}
public void setRole(int role) {
this.role = role;
}
}
在 AgoraRTMRepository 中重复此步骤,你的文件应如下:
package com.sandeep.agoratoken;
public class AgoraRTMRepository {
private static String appId = "APP_ID";
private static String appCertificate = "APP_CERT";
private String userId = "USER_ID";
private int expireTimestamp = 0;
public static String getAppId() {
return appId;
}
public static String getAppCertificate() {
return appCertificate;
}
public static void setAppCertificate(String appCertificate) {
AgoraRTMRepository.appCertificate = appCertificate;
}
public String getUserId() {
return userId;
}
public int getExpireTimestamp() {
return expireTimestamp;
}
public static void setAppId(String appId) {
AgoraRTMRepository.appId = appId;
}
}
看到类似内容后,单击“获取器和设置器”,然后全选。至此,声网 Token Server 大致构建完成。
输入内容并定义 Token Server RTC POST Request:
package com.sandeep.agoratoken;
import com.sandeep.agoratoken.media.RtcTokenBuilder;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.json.simple.JSONObject;
@Path("api")
public class Agora{
@POST
@Path("rtc")
@Produces(MediaType.APPLICATION_JSON)
public Object getRTCToken(AgoraRepository resource) {
}
}
初始化所有参数:
package com.sandeep.agoratoken;
import com.sandeep.agoratoken.media.RtcTokenBuilder;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.json.simple.JSONObject;
@Path("api")
public class Agora{
@POST
@Path("rtc")
@Produces(MediaType.APPLICATION_JSON)
public Object getRTCToken(AgoraRepository resource) {
RtcTokenBuilder token = new RtcTokenBuilder();
String channelName = resource.getChannelName();
int expireTime = resource.getExpirationTimeInSeconds();
RtcTokenBuilder.Role role = RtcTokenBuilder.Role.Role_Subscriber;
int uid = resource.getUid();
}
}
创建 Token 之前先检查一下,确保万无一失:
package com.sandeep.agoratoken;
import com.sandeep.agoratoken.media.RtcTokenBuilder;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.json.simple.JSONObject;
@Path("api")
public class Agora{
@POST
@Path("rtc")
@Produces(MediaType.APPLICATION_JSON)
public Object getRTCToken(AgoraRepository resource) {
RtcTokenBuilder token = new RtcTokenBuilder();
String channelName = resource.getChannelName();
int expireTime = resource.getExpirationTimeInSeconds();
RtcTokenBuilder.Role role = RtcTokenBuilder.Role.Role_Subscriber;
int uid = resource.getUid();
// check for null channelName
if (channelName==null){
JSONObject error=new JSONObject();
error.put("error","Channel ID cannot be blank");
return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
}
if(expireTime==0){
expireTime = 3600;
}
if(resource.getRole()==1){
role = RtcTokenBuilder.Role.Role_Publisher;
}else if(resource.getRole()==0){
role = RtcTokenBuilder.Role.Role_Attendee;
}
}
}
创建一个时间戳并生成令牌:
package com.sandeep.agoratoken;
import com.sandeep.agoratoken.media.RtcTokenBuilder;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.json.simple.JSONObject;
@Path("api")
public class Agora{
@POST
@Path("rtc")
@Produces(MediaType.APPLICATION_JSON)
public Object getRTCToken(AgoraRepository resource) {
RtcTokenBuilder token = new RtcTokenBuilder();
String channelName = resource.getChannelName();
int expireTime = resource.getExpirationTimeInSeconds();
RtcTokenBuilder.Role role = RtcTokenBuilder.Role.Role_Subscriber;
int uid = resource.getUid();
// check for null channelName
if (channelName==null){
JSONObject error=new JSONObject();
error.put("error","Channel ID cannot be blank");
return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
}
if(expireTime==0){
expireTime = 3600;
}
if(resource.getRole()==1){
role = RtcTokenBuilder.Role.Role_Publisher;
}else if(resource.getRole()==0){
role = RtcTokenBuilder.Role.Role_Attendee;
}
int timestamp = (int)(System.currentTimeMillis() / 1000 + expireTime);
String result = token.buildTokenWithUid(resource.appId, resource.appCertificate,
channelName, uid, role, timestamp);
System.out.print(result);
JSONObject jsondict = new JSONObject();
jsondict.put("message",result);
return jsondict;
}
}
好啦!声网 Java RTC 令牌服务器创建完成!是时候启动声网 RTM 令牌服务器了!
将以下代码添加到声网类中:
@POST
@Path("rtm")
@Produces(MediaType.APPLICATION_JSON)
public Object getRTMToken(AgoraRTMRepository resource) throws Exception {
String userId = resource.getUserId();
if (userId==null){
JSONObject error=new JSONObject();
error.put("error","User ID cannot be blank");
return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
}
RtmTokenBuilder token = new RtmTokenBuilder();
String result = token.buildToken(resource.getAppId(), resource.getAppCertificate(), userId, Role.Rtm_User, resource.getExpireTimestamp());
System.out.println(result);
JSONObject jsondict = new JSONObject();
jsondict.put("message",result);
return jsondict;
}
最终代码应如下:
package com.sandeep.agoratoken;
import com.sandeep.agoratoken.media.RtcTokenBuilder;
import com.sandeep.agoratoken.rtm.RtmTokenBuilder;
import com.sandeep.agoratoken.rtm.RtmTokenBuilder.Role;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.json.simple.JSONObject;
@Path("api")
public class Agora{
@POST
@Path("rtc")
@Produces(MediaType.APPLICATION_JSON)
public Object getRTCToken(AgoraRepository resource) {
RtcTokenBuilder token = new RtcTokenBuilder();
String channelName = resource.getChannelName();
int expireTime = resource.getExpirationTimeInSeconds();
RtcTokenBuilder.Role role = RtcTokenBuilder.Role.Role_Subscriber;
int uid = resource.getUid();
// check for null channelName
if (channelName==null){
JSONObject error=new JSONObject();
error.put("error","Channel ID cannot be blank");
return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
}
if(expireTime==0){
expireTime = 3600;
}
if(resource.getRole()==1){
role = RtcTokenBuilder.Role.Role_Publisher;
}else if(resource.getRole()==0){
role = RtcTokenBuilder.Role.Role_Attendee;
}
int timestamp = (int)(System.currentTimeMillis() / 1000 + expireTime);
String result = token.buildTokenWithUid(resource.appId, resource.appCertificate,
channelName, uid, role, timestamp);
System.out.print(result);
JSONObject jsondict = new JSONObject();
jsondict.put("message",result);
return jsondict;
}
@POST
@Path("rtm")
@Produces(MediaType.APPLICATION_JSON)
public Object getRTMToken(AgoraRTMRepository resource) throws Exception {
String userId = resource.getUserId();
if (userId==null){
JSONObject error=new JSONObject();
error.put("error","User ID cannot be blank");
return Response.status(Response.Status.BAD_REQUEST).entity(error).build();
}
RtmTokenBuilder token = new RtmTokenBuilder();
String result = token.buildToken(resource.getAppId(), resource.getAppCertificate(), userId, Role.Rtm_User, resource.getExpireTimestamp());
System.out.println(result);
JSONObject jsondict = new JSONObject();
jsondict.put("message",result);
return jsondict;
}
}
接下里是最后一步——部署!
部署
首先,创建一个 WAR 文件。从右侧切换栏打开 Maven,单击 Maven 图标执行命令:
运行 mvn clean package
只需一两分钟就能生成一个 WAR 文件,大家可以看到 WAR 文件的位置。
在资源库中创建一个 Dockerfile,并将你的代码推送到 GitHub。
FROM tomcat:9.0.37
MAINTAINER SaiSandeep
COPY target/agoratoken.war /usr/local/tomcat/webapps/ROOT.war
EXPOSE 8080
CMD ["catalina.sh", "run"]
创建一个 Ubuntu VM。
运行以下命令,在 Linux 上安装 Docker:
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
Mac 和 Windows 用户可以从 Docker 官方网站中获取可执行文件。
运行此命令,检查你的设备是否已安装 Docker:
-> docker -v
将你的资源库克隆到 VM。并运行下列命令:
nano src/main/java/com/sandeep/agoratoken/MyResourceRepository.java
填写你的 APP_ID 和 APP_CERT(可以从声网控制台获取 APP_ID 和 APP_CERT)。
运行以下命令来构建 Docker 映像:
docker build -t agoratoken ./
OK,运行!
docker run -p 80:8080 agoratoken
检查 API。
更多资源
想了解声网应用程序令牌的更多信息,请参阅《设置身份验证指南》和《声网高级指南:如何构建令牌(Go)》 。
Github 资源库:声网 Token 参考代码
原文作者:声网 SuperStar
原文链接:https://www.agora.io/en/blog/building-an-agora-token-server-using-java/