Token鉴权

Token是阿里云设计的一种安全保护签名,目的是为了阻止恶意攻击者盗用您的云服务使用权。您需要在服务端生成Token并下发给客户端,客户端SDK会基于Token发起入会请求,鉴权通过则成功入会。

前提条件

  • 已注册阿里云账号,并开通阿里云视频直播服务。

  • 创建实时音视频应用,获取实时音视频应用的AppIDAppKey,详情请参见创建应用

实现Token鉴权

Token使用流程

image
  1. 客户端向AppServer申请Token,AppServer根据规则生成Token并返回。

  2. 客户端使用Token、appid、channelId、userId 等信息,加入指定频道。

  3. 阿里云RTC服务验证Token,客户端成功加入频道。

Token计算方法

阿里云官方为Go/Java/Python等主流语言提供了代码实现示例:Token生成代码示例

Token生成相关字段说明:

参数名称

描述

AppID

实时音视频应用ID和应用Key,在直播控制台创建实时音视频应用后会自动生成,详细请参见获取应用开发参数

AppKey

ChannelID

频道ID,由用户自定义,String类型,不能使用Long类型,支持数字、大小写字母、短划线(-)、下划线(_),不超过64个字符。主播和连麦观众需使用同一个房间号。

UserId

用户ID,由用户自定义,String类型,不能使用Long类型,支持数字、大小写字母、短划线(-)、下划线(_),长度不超过64个字符。

Nonce

Nonce字符串可以为空,这里推荐为空

Timestamp

过期时间戳(秒级)。不能超过24小时,建议默认24小时。如果设置为24小时,则取当前秒数后再增加24*60*60。

Token生成过程示例

yuque_diagram (1)

Token生成代码示例:

// 1. 拼接字段:AppID+AppKey+ChannelID+UserID+Nonce+Timestamp
// 2. 使用sha256函数计算拼接字段,生成token。
token = sha256(AppID+AppKey+ChannelId+UserID+Nonce+timestamp)
//示例:
AppID = "abc",AppKey="abckey",ChannelID="abcChannel",UserID="abcUser",Nonce="",Timestamp=1699423634
token = sha256("abcabckeyabcChannelabcUser1699423634") = "3c9ee8d9f8734f0b7560ed8022a0590659113955819724fc9345ab8eedf84f31"

实时音视频场景

介绍ARTC场景下如何使用Token对客户端加入频道的用户进行鉴权,ARTC SDK提供了单参入会和多参入会两种方式,下面以AndroidiOS为例。

单参数入会(推荐)

ARTC场景下提供单参数入会的API。单参数入会的形式主要是为了解决客户自身服务器与App传入参数不一致引起入会失败而开发的类似语法糖的使用方式。单参数入会是通过把鉴权TokenAppID、 ChannelIDNonceUserIDTimestamp通过JSON组织起来,然后把JSON字符串进行Base64编码后算出一个新的鉴权字符串(Base64 Token)。

说明

后续给阿里云反馈问题时需要提供Base64 Token或者传入的UserName。

yuque_diagram (3)

Base64 Token生成代码示例

Java 代码示例

package com.example;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Calendar;
import org.json.JSONObject;

public class App {

    public static String createBase64Token(String appid, String appkey, String channelid, String userid) {
        // Calculate the expiration timestamp (24 hours from now)
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR_OF_DAY, 24);
        long timestamp = calendar.getTimeInMillis() / 1000;

        // Concatenate the strings
        String stringBuilder = appid + appkey + channelid + userid + timestamp;

        // Calculate the SHA-256 hash
        String token = sha256(stringBuilder);

        // Create the JSON object
        JSONObject base64tokenJson = new JSONObject();
        base64tokenJson.put("appid", appid);
        base64tokenJson.put("channelid", channelid);
        base64tokenJson.put("userid", userid);
        base64tokenJson.put("nonce", "");
        base64tokenJson.put("timestamp", timestamp);
        base64tokenJson.put("token", token);

        // Convert the JSON object to a string and encode it in Base64
        String jsonStr = base64tokenJson.toString();
        String base64token = Base64.getEncoder().encodeToString(jsonStr.getBytes(StandardCharsets.UTF_8));
        return base64token;
    }

    private static String sha256(String input) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1)
                    hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        String appid = "your_appid";
        String appkey = "your_appkey";
        String channel_id = "your_channel_id";
        String user_id = "your_user_id";

        String base64token = createBase64Token(appid, appkey, channel_id, user_id);
        System.out.println("Base64 Token: " + base64token);
    }
}

Go 代码示例

package main

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"time"
)

func createBase64Token(appid, appkey, channelID, userID string) (string, error) {
	// Calculate the expiration timestamp (24 hours from now)
	timestamp := time.Now().Add(24 * time.Hour).Unix()

	// Concatenate the strings
	stringBuilder := appid + appkey + channelID + userID + fmt.Sprintf("%d", timestamp)

	// Calculate the SHA-256 hash
	hasher := sha256.New()
	hasher.Write([]byte(stringBuilder))
	token := hasher.Sum(nil)

	// Convert the hash to a hexadecimal string using encoding/hex
	tokenHex := hex.EncodeToString(token)

	// Create the JSON object
	tokenJSON := map[string]interface{}{
		"appid":     appid,
		"channelid": channelID,
		"userid":    userID,
		"nonce":     "",
		"timestamp": timestamp,
		"token":     tokenHex,
	}

	// Convert the JSON object to a string and encode it in Base64
	jsonBytes, err := json.Marshal(tokenJSON)
	if err != nil {
		return "", err
	}
	base64Token := base64.StdEncoding.EncodeToString(jsonBytes)

	return base64Token, nil
}

func main() {
	appid := "your_appid"
	appkey := "your_appkey"
	channelID := "your_channel_id"
	userID := "your_user_id"

	token, err := createBase64Token(appid, appkey, channelID, userID)
	if err != nil {
		fmt.Println("Error creating token:", err)
		return
	}
	fmt.Println("Base64 Token:", token)
}

Python 代码示例

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import hashlib
import datetime
import time
import base64
import json

def create_base64_token(app_id, app_key, channel_id, user_id):
    expire = datetime.datetime.now() + datetime.timedelta(days=1)
    timestamp = int(time.mktime(expire.timetuple()))
    h = hashlib.sha256()
    h.update(str(app_id).encode('utf-8'))
    h.update(str(app_key).encode('utf-8'))
    h.update(str(channel_id).encode('utf-8'))
    h.update(str(user_id).encode('utf-8'))
    h.update(str(timestamp).encode('utf-8'))
    token = h.hexdigest()

    jsonToken = {'appid':app_id,
                 'channelid':channel_id,
                 'userid':user_id,
                 'nonce':'',
                 'timestamp':timestamp,
                 'token':token
                }
    base64Token = base64.b64encode(json.dumps(jsonToken).encode())

    return base64Token

def main():
    app_id = 'your_appid'
    app_key = 'your_appkey'
    channel_id = 'your_channel_id'
    user_id = 'your_user_id'

    base64Token = create_base64_token(app_id, app_key, channel_id, user_id)
    
    print(base64Token)

if __name__ == '__main__':
    main()

Node.js代码示例

'use strict'
const crypto = require('crypto')
function create_base64_token(appid, appkey, channelid, userid) {
    let timestamp = Math.floor(Date.now() / 1000 + 24 * 60 * 60)

    let string_builder = appid + appkey + channelid + userid + timestamp.toString()
    let token = crypto.createHash('sha256').update(string_builder).digest('hex')
    let base64tokenJson = {
        appid:appid,
        channelid:channelid,
        userid:userid,
        nonce:'',
        timestamp:timestamp,
        token:token
    }
    let base64token = Buffer.from(JSON.stringify(base64tokenJson), 'utf-8').toString('base64')
    return base64token
}

let appid = "your_appid";
let appkey = "your_appkey";
let channel_id = "your_channel_id";
let user_id = "your_user_id";

let base64token = create_base64_token(appid, appkey, channel_id, user_id)
console.log(base64token)

Rust代码示例

use chrono::{Duration, Utc};
use sha2::{Sha256, Digest};
use serde_json::json;
use base64::encode;

fn create_base64_token(appid: &str, appkey: &str, channel_id: &str, user_id: &str) -> String {
    // Calculate the expiration timestamp (24 hours from now)
    let timestamp = (Utc::now() + Duration::hours(24)).timestamp();

    // Concatenate the strings
    let string_builder = format!("{}{}{}{}{}", appid, appkey, channel_id, user_id, timestamp);

    // Calculate the SHA-256 hash
    let mut hasher = Sha256::new();
    hasher.update(string_builder);
    let token = hasher.finalize();
    let token_hex = format!("{:x}", token);

    // Create the JSON object
    let token_json = json!({
        "appid": appid,
        "channelid": channel_id,
        "userid": user_id,
        "nonce": "",
        "timestamp": timestamp,
        "token": token_hex
    });

    // Convert the JSON object to a string and encode it in Base64
    let base64_token = encode(token_json.to_string());

    base64_token
}

fn main() {
    let appid = "your_appid";
    let appkey = "your_appkey";
    let channel_id = "your_channel_id";
    let user_id = "your_user_id";

    let token = create_base64_token(appid, appkey, channel_id, user_id);
    println!("Base64 Token: {}", token);
}

App调用API示例

客户端从服务端获取Base64 Token后,加入房间。

  • Android端:

    // channelid, userid传入null即可;客户端如果要传入channelid、userid但是必须要和产生token用的一致,客户可以使用这个特性进行校验服务器和客户端参数是否一致
    // base64Tokenbase64后的token
    // username是方便排查问题时客户传入的标识
    mAliRtcEngine.joinChannel(base64Token, null, null, "username");
  • iOS端:

    // channelid, userid传入null即可;客户端如果要传入channelid、userid但是必须要和产生token用的一致,客户可以使用这个特性进行校验服务器和客户端参数是否一致
    // base64Tokenbase64后的token
    // username是方便排查问题时客户传入的标识
    [self.engine joinChannel:base64Token channelId:nil userId:nil name:@"username" onResultWithUserId:nil];

多参数入会

ARTC 场景下仍然保留多参入会的 API,通过 AliRtcAuthInfo 数据结构保存用户的多参入会 Token 及用户信息进行鉴权。

重要

加入频道时使用的频道 ID 和用户 ID 必须与生成 Token 时的一致。

  • Android端:

    // 需要传入多参入会token及用户信息
    AliRtcAuthInfo authInfo = new AliRtcAuthInfo();
    authInfo.appId = appId;
    authInfo.channelId = channelId;
    authInfo.userId = userId;
    authInfo.timestamp = timestamp;
    authInfo.nonce = nonce;
    authInfo.token = token;
    // 入会
    mAliRtcEngine.joinChannel(authInfo, "");
  • iOS端:

    // 需要传入多参入会token及用户信息
    let authInfo = AliRtcAuthInfo()
    authInfo.appId = appId
    authInfo.channelId = channelId
    authInfo.nonce = nonce
    authInfo.userId = userId
    authInfo.timestamp = timestamp
    authInfo.token = authToken
    // 入会
    self.rtcEngine?.joinChannel(authInfo, name: nil)

直播连麦场景

TokenAppID、 ChannelIDNonceUserIDTimestamp添加到直播连麦URLquery中,传入SDK即可。URL包含的详细字段请参见直播连麦地址规则

image

示例URL

连麦/主播PK场景推流地址:

artc://live.aliyun.com/push/633?timestamp=1685094092&token=fe4e674ade****6686&userId=718&sdkAppId=xxx

连麦/主播PK场景拉流地址:

artc://live.aliyun.com/play/633?timestamp=1685094092&token=fe4e674ade****6686&userId=718&sdkAppId=xxx
说明

live.aliyun.com是直播连麦URL固定前缀,不是一个真实的域名,不可对其进行域名的相关操作(比如ping,traceroute,telnet等)。