外部授權
功能介紹
1、當需要觀眾登入機構的使用者系統,驗證通過後才能進入直播觀看頁時,可使用外部授權。驗證觀眾是否可觀看由機構實作,驗證通過後開啟的觀看頁面由保利威實作。
2、機構後台針對通過驗證的觀眾,開啟直播觀看頁時 URL 需帶上指定的參數,經過直播系統驗證請求合法後,直播系統會呼叫客戶在後台設定的自訂授權驗證介面,進行介面授權驗證,兩次驗證通過,才能進入直播觀看頁,並且介面回傳的觀眾帳號具有唯一性,即同一個帳號不能在兩個地方同時登入,較早登入的帳號會被踢出。

1、Secretkey:用於校驗簽章的產生。
2、自訂 URL:用於外部授權驗證的 API 介面。
3、跳轉位址:觀眾直接存取 Polyv 觀看頁,會跳轉到該位址;若跳轉位址為空,則顯示預設提示頁。
展示效果
https://demo.ipolyv.cn/chenwb/open.php
外部授權流程詳解
1、在自訂 URL 處填寫使用者的授權驗證 API 介面,需要完整的不帶參數的 url 位址(不能是 localhost 等本地伺服器位址,且不能帶 ? 號),如:http://myWebsite.com/auth
2、在請求保利威視直播觀看頁時需帶上 userid(使用者 ID,僅支援英文大小寫、數字和底線)、ts(目前時間的毫秒級時間戳記)和 sign(用於校驗的簽章,產生規則是 secretkey + userid + secretkey + ts 進行 MD5 加密),如 https://live.polyv.cn/watch/125527?userid=6b3a43&ts=1498547407000&sign=dd9dc9e42ad7c0204398e925a4ee0f46
3、直播系統會對字串 secretkey+userid+secretkey+ts 進行 MD5 加密後與使用者提交的 sign 參數的值做比較判斷是否合法。一次成功請求後,該連結將失效(sign 只能成功使用一次)。如果合法,直播系統將呼叫使用者的 api 介面,並把 userid(使用者 ID)、ts(目前時間的毫秒級時間戳記)、channelId(頻道號)和 token(用於校驗的簽章)四個參數透過 GET 請求傳給使用者。如果不合法,則給出錯誤提示。
4、使用者 API 介面取得 userid、ts 和 token 參數後,進行簽章驗證。如果驗證通過,則將學員相關資訊和可選的觀看權限 watchAccess(詳參「使用者系統回傳觀眾資訊回應參數描述」)回傳給直播系統。
5、直播系統接收使用者 API 介面回傳的資料,如果驗證成功,則進入到保利威視直播觀看頁,聊天區將顯示學員的暱稱和頭像。watchAccess 為 trial 時,直播系統將結合頻道的外部授權試看配置判斷是否進入試看;如果驗證失敗,則給出錯誤提示。
互動圖如下

參數描述
請求觀看頁所帶參數
使用者將以下的參數提交給直播的觀看頁,(例如:https://live.polyv.cn/watch/125527?userid=test&ts=1565948760108&sign=b0b6eb22b6fa5e5684873052c27a6cef)
直播系統會對 sign 進行驗證,判斷是否合法,一次成功請求後,該連結將失效(sign 只能成功使用一次)
| 參數名 | 必選 | 說明 |
|---|---|---|
| userid | true | 觀眾 id,重複 id 時先登入的觀眾會被踢出直播間,【僅支援英文大小寫、數字和底線,長度最大 64 位字元,超出 64 位的部分將被截取不做記錄】 |
| ts | true | 13 位毫秒級時間戳記 |
| sign | true | 使用者校驗的簽章(字母小寫),加密規則:secretkey + userid + secretkey + ts |
| vid | false | 回放影片 id,如需回放某個回放影片,則需要傳該參數,如:e07738ddd6 該值可使用介面【查詢影片庫列表】回傳的 videoId |
觀看頁請求觀眾資訊介面參數
觀看頁請求使用者在後台設定的自訂 URL 取得觀眾資訊,以下為請求所帶的參數
| 參數名 | 必填 | 參數說明 |
|---|---|---|
| userid | true | 觀眾 id,重複 id 時先登入的觀眾會被踢出直播間,【僅支援英文大小寫、數字和底線,長度最大 64 位字元】 |
| channelId | true | 頻道號 |
| ts | true | 目前時間的 13 位毫秒級時間戳記 |
| token | true | 用於校驗的簽章,產生的規則:對字串 secretkey + userid + secretkey + ts 進行 MD5 加密產生的字串(字母小寫) |
使用者系統回傳觀眾資訊回應參數描述
| 字段 | 類型 | 必填 | 字段說明 |
|---|---|---|---|
| status | int | true | 請求結果,1 表示成功,0 表示失敗 |
| userid | string | true | 觀眾 id,【僅支援英文大小寫、數字和底線,長度最大 64 位字元】 |
| nickname | string | true | 觀眾暱稱 |
| marqueeName | string | false | 自訂跑馬燈字段,該字段會透過【URL 自訂跑馬燈】中 code 參數回調 |
| avatar | string | true | 觀眾頭像位址,頭像尺寸 200*200,體積不超過 30KB。 |
| actor | string | false | 觀眾頭銜 |
| actorFColor | string | false | 觀眾頭銜字體顏色,非必須,請使用 CSS Hex 值並且帶 # 號 |
| actorBgColor | string | false | 觀眾頭銜背景顏色,非必須,請使用 CSS Hex 值帶 # 號 |
| param4 | string | false | 統計觀眾觀看日誌的自訂參數 |
| param5 | string | false | 統計觀眾觀看日誌的自訂參數 |
| errorUrl | string | false | 請求失敗時觀看頁跳轉的位址(會帶上 channelId 和 userid) |
| userTags | string 陣列 | false | 觀眾業務標籤,用於問卷定向彈出匹配(僅外部授權場景生效)。支援傳入一個或多個標籤;客戶建立問卷配置匹配條件後,攜帶任一匹配標籤的觀眾進入直播間將彈出對應問卷,未攜帶或為空時不彈出 |
| watchAccess | string | false | 外部授權觀看權限。allow:正式觀看;trial:結合頻道的外部授權試看配置判斷是否進入試看。未傳、空值或其他值按 allow 處理。該字段僅在 status 為 1 時生效 |
回應範例
成功範例
{
"status":1,
"userid":"2qwerty",
"nickname":"testNick",
"actor":"paul",
"actorFColor":"#123123",
"actorBgColor":"#FFFFFF",
"param4":"param4test",
"userTags":["vip","registered"],
"watchAccess":"trial",
"avatar":"http://live.polyv.net/assets/images/avatars/9avatar.jpg"
}
異常範例
{
"status":0,
"errorUrl":"http://test.com"
}
外部授權試看
頻道可透過修改頻道資訊介面配置外部授權試看開關、試看時長、有效截止時間和試看結束跳轉位址,對應字段如下:
| 字段 | 說明 |
|---|---|
| extTrialWatchEnabled | 外部授權試看開關,Y:開啟,N:關閉 |
| extTrialWatchTime | 試看時長,單位為分鐘 |
| extTrialWatchEndTime | 試看有效截止時間,不傳表示永久有效 |
| extTrialRedirectUrl | 試看耗盡後的跳轉位址 |
以上字段屬於頻道配置,不需要在客戶外部授權介面的回應中回傳。客戶外部授權介面只需透過 watchAccess 表達目前觀眾的觀看權限:
| watchAccess | 處理方式 |
|---|---|
| allow | 正式觀看,不受外部授權試看時長限制 |
| trial | 結合頻道外部授權試看配置判斷是否進入試看 |
| 未傳、空值或其他值 | 按 allow 處理,保持原有外部授權邏輯 |
status 的優先級高於 watchAccess:status 不為 1 時仍按外部授權失敗處理;只有 status 為 1 時 watchAccess 才會生效。watchAccess 為 trial 但頻道未開啟外部授權試看、試看配置已過期或配置不完整時,不進入試看限制。
付費或業務完成後的狀態刷新
客戶完成付費、報名、資料補充或認證後,應使該觀眾再次請求客戶外部授權介面時回傳 watchAccess=allow,例如:
{
"status":1,
"userid":"2qwerty",
"nickname":"testNick",
"watchAccess":"allow",
"avatar":"http://live.polyv.net/assets/images/avatars/9avatar.jpg"
}
同一瀏覽器工作階段內,目前直播場次已經存在試看記錄時,觀眾返回原觀看頁並重新取得頻道觀看資訊,直播系統會再次請求客戶外部授權介面。該重新整理不要求試看時長已經耗盡,也不要求頻道目前仍在直播中;客戶回傳 allow 後,觀眾切換為正式觀看。回傳 trial 或介面呼叫失敗時,繼續保持目前試看狀態。
客戶外部授權介面應支援重複、冪等呼叫,並根據 userid 查詢最新業務狀態。短時間內的重複重新整理可能被合併,客戶不應依賴每次觀看頁請求都產生一次外部授權回調。同一瀏覽器工作階段仍然有效時,不需要重新產生帶 ts、sign 的觀看位址;工作階段失效、跨瀏覽器或更換裝置時,仍需重新執行完整外部授權流程。
程式碼範例(JAVA)
註:LiveSignUtil 屬於直播 SDK,如不使用直播 SDK 可使用以下第三點中的「MD5 簽名方法」。
1、使用者系統產生觀看連結
public static void main(String[] args) {
//TODO 设置频道号
String channelId = "2275495";
//TODO 设置externalKey
String secret = "";
// TODO 设置直播观看页地址
// 如果使用定制域名,可在“查询频道信息”接口中获取到频道的观看地址 watchUrl
// https://help.polyv.net/index.html#/live/api/v4/channel/operate/get_channel_detail
String url = "https://live.polyv.cn/watch/"+channelId;
// TODO 根据实际情况设置userid
String userid = "sadboy";
String ts = String.valueOf(System.currentTimeMillis());
String signText = secret+ userid +secret+ts;
try {
String sign = LiveSignUtil.md5Hex(signText);
url += "?userid="+userid+"&ts="+ts+"&sign="+sign;
log.info(url);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
2、使用者伺服器校驗 polyv 直播系統回調
@Slf4j
@Controller
@RequestMapping(value = "/polyv")
public class PolyvController {
//TODO 修改secretKey
private static final String secret = "******";
@GetMapping("external")
@ResponseBody
public Map<String, Object> external(String channelId,String userid, Long ts, String token) {
Assert.assertNotBlack(userid);
Assert.assertNotBlack(token);
Assert.assertNotNull(ts);
HashMap<String, Object> map = new HashMap<>();
long timeMillis = System.currentTimeMillis();
long diffTime = Math.abs(timeMillis - ts);
//1、时间戳判断
if (diffTime > 5 * 60 * 1000) {
log.error("时间戳验证错误");
map.put("status", 0);
//抛出异常时,如果设置errorUrl,则会跳转到 errorUrl ,如果未返回 errorUrl,则先查询 外部授权参数
// externalRedirectUri,externalRedirectUri不为空则跳转externalRedirectUri地址,externalRedirectUri为空则跳转保利威默认页面
map.put("errorUrl", "https://www.polyv.net");
return map;
}
//2、签名判断
String signText = secret + userid + secret + ts;
String sign = null;
try {
sign = LiveSignUtil.md5Hex(signText);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (sign == null || !sign.equals(token)) {
log.error("签名验证错误");
map.put("status", 0);
//抛出异常时,如果设置errorUrl,则会跳转到 errorUrl ,如果未返回 errorUrl,则先查询 外部授权参数
// externalRedirectUri,externalRedirectUri不为空则跳转externalRedirectUri地址,externalRedirectUri为空则跳转保利威默认页面
map.put("errorUrl", "https://www.polyv.net");
return map;
}
//TODO 业务逻辑处理,后续需根据具体需求进行数据库操作
//3、正常返回
map.put("status", 1);
map.put("userid", userid);
map.put("nickname", "保利威测试用户");
map.put("marqueeName", "保利威测试跑马灯");
map.put("actor", "学生");
map.put("actorFColor", "#2469f3");
map.put("actorBgColor", null);
map.put("param4", null);
map.put("param5", null);
// 未完成付费或业务流程时返回trial,完成后返回allow
map.put("watchAccess", "trial");
map.put("avatar", "https://help.polyv.net/favicon.ico");
return map;
}
}
3、MD5 簽名方法
/**
* 对字符串做MD5加密,返回加密后的字符串。
* @param text 待加密的字符串。
* @return 加密后的字符串。
* @throws NoSuchAlgorithmException 签名异常
* @throws UnsupportedEncodingException 编码异常
*/
public static String md5Hex(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] inputByteArray = text.getBytes(LiveConstant.UTF8);
messageDigest.update(inputByteArray);
byte[] resultByteArray = messageDigest.digest();
return byteArrayToHex(resultByteArray).toLowerCase();
}
/**
* 将字节数组换成成16进制的字符串
* @param byteArray 字节
* @return 字符串
*/
public static String byteArrayToHex(byte[] byteArray) {
// 初始化一个字符数组用来存放每个16进制字符
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
// new一个字符数组,这个就是用来组成结果字符串的(一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方))
char[] resultCharArray = new char[byteArray.length * 2];
// 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
int index = 0;
for (byte b : byteArray) {
resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
resultCharArray[index++] = hexDigits[b & 0xf];
}
// 字符数组组合成字符串返回
return new String(resultCharArray);
}
程式碼範例(PHP)
<?php
header("Content-type:application/json;charset=UTF-8"); //媒体格式类型为JSON数据格式
$secretkey = "aDrOt0Cpy8";
$userid = isset($_GET["userid"]) ? $_GET["userid"] : "";
$ts = isset($_GET["ts"]) ? $_GET["ts"] : "";
$channelId = isset($_GET["channelId"]) ? $_GET["channelId"] : "";
$token = isset($_GET["token"]) ? $_GET["token"] : "";
$sign = md5($secretkey . $userid . $secretkey . $ts);
//用户进行授权验证,返回对应的数据(json格式)
if ($sign == $token) {
//验证正确
$array1 = array(
"status" => 1, //返回状态
"userid" => $userid, //学员唯一标识
"nickname" => "保利威", //学员昵称
"marqueeName" => "polyv", //自定义跑马灯字段
"avatar" => "http://live.polyv.net/assets/images/avatars/9avatar.jpg", //学员头像
"actor" => "VIP", // 学员头衔,可以不传递
"actorFColor" => "#5C96E5", // 学员头衔字体颜色,可以不传递
"actorBgColor" => "#FFFFFF", // 学员头衔背景颜色,可以不传递
"watchAccess" => "trial" // 未完成付费或业务流程时返回trial,完成后返回allow
);
$json1 = json_encode($array1);
echo $json1;
} else {
//验证错误
$array0 = array(
"status" => 0,
"errorUrl" => "http://xxx.xx.xxxx/error.html", //验证错误跳转的自定义页面
);
$json0 = json_encode($array0);
echo $json0;
}
注意事項
1、要保證自訂驗證介面回傳的 userid 的唯一性,當多個觀眾使用同一個 userid 進入觀看頁時,較早登入的觀眾會被後面登入的觀眾踢出,觀看頁會提示「帳號在另外的地方登入,您將被退出觀看。如下圖:

2、自訂驗證介面需要填寫完整的 URL 位址,且不能是 localhost 等本地伺服器位址。
3、自訂驗證介面回傳給直播系統的資料格式是 json 格式。
4、同時傳入了 nickname 和 marqueeName,在觀看日誌中,marqueeName 將作為使用者暱稱的統計字段。
5、watchAccess 僅在 status 為 1 時生效。客戶介面應支援重複、冪等呼叫,並根據 userid 回傳最新的觀看權限。
錯誤提示
1、user not found:請求自訂驗證介面錯誤,或者介面回傳的格式不對。
2、invalid sign:簽名錯誤,sign 的產生規則是 secretkey+userid+secretkey+ts 進行 MD5 加密。
3、sign expired:簽名過期,每一個 sign 只能使用一次,使用後需重新產生。
