# 外部授权

## 功能介绍

1、当需要观众登录机构的用户系统，验证通过后才能进入直播观看页时，可使用外部授权。验证观众是否可观看由机构实现，验证通过后打开的观看页面由保利威实现。

2、机构后台针对通过验证的观众，打开直播观看页时URL需带上指定的参数，经过直播系统验证请求合法后，直播系统会调用客户在后台设置的自定义授权验证接口，进行接口授权验证，两次验证通过，才能进入直播观看页，并且接口返回的观众账号具有唯一性，即同一个账号不能在两个地方同时登录，较早登录的账号会被踢出。

![](/files/YHaVuq3OaTtbXlYUi3ov)

1、Secretkey：用于校验签名的生成。

2、自定义URL：用于外部授权验证的API接口。

3、跳转地址：观众直接访问Polyv观看页，会跳转到该地址；若跳转地址为空，则显示默认提示页。

## 展示效果

<http://demo.polyv.net/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参数后，进行签名验证。如果验证通过，则将学员相关信息（详参“用户系统返回观众信息json说明”）返回给直播系统。

5、直播系统接收用户API接口返回的数据，如果验证成功，则进入到保利威视直播观看页，聊天区将显示学员的昵称和头像。如果验证失败，则给出错误提示。

**交互图如下**

![](/files/dYU6eY93f44yJxxfj1EZ)

## 参数描述

### 请求观看页所带参数

用户将以下的参数提交给直播的观看页，（例如：<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 | <p>回放视频id，如需回放某个回放视频，则需要传该参数，如：e07738ddd6<br>该值可使用接口【<a href="https://git.polyv.net/help-center/document-center/-/blob/master/live/api/channel/playback/get_playback_list/README.md">查询视频库列表</a>】返回的videoId</p> |

### 观看页请求观众信息接口参数

观看页请求用户在后台设置的自定义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自定义跑马灯](https://git.polyv.net/help-center/document-center/-/blob/master/live/api/player/marquee_url_instruction/README.md)】中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）                                                                                                                       |

### 响应示例

成功示例

```json
{
    "status":1,
    "userid":"2qwerty",
    "nickname":"testNick",
    "actor":"paul",
    "actorFColor":"#123123",
    "actorBgColor":"#FFFFFF",
    "param4":"param4test",
    "avatar":"http://live.polyv.net/assets/images/avatars/9avatar.jpg"
}
```

异常示例

```json
{
    "status":0,
    "errorUrl":"http://test.com"
}
```

## 代码示例（JAVA）

注：LiveSignUtil属于[直播SDK](https://git.polyv.net/help-center/document-center/-/blob/master/live/java/README.md)，如不使用直播SDK可使用以下第三点中的“MD5签名方法”。

1、用户系统生成观看链接

```java
    public static void main(String[] args) {
        //TODO 设置频道号
        String channelId = "2275495";
        //TODO 设置externalKey
        String secret = "";
        //根据实际情况设置userid
        String userid = "sadboy";
        String ts = String.valueOf(System.currentTimeMillis());
        String url = "https://live.polyv.cn/watch/"+channelId;
        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直播系统回调

```java
@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);
        map.put("avatar", "https://help.polyv.net/favicon.ico");
        return map;
    }
    
    
}
```

3、MD5签名方法

```java
    /**
     * 对字符串做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）

```java
<?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"   // 学员头衔背景颜色，可以不传递
    );
    $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进入观看页时，较早登录的观众会被后面登录的观众踢出，观看页会提示 "帐号在另外的地方登录,您将被退出观看。如下图：

![](/files/vUsO7154Fzi5CQAalOPX)

2、自定义验证接口需要填写完整的URL地址，且不能是localhost等本地服务器地址。

3、自定义验证接口返回给直播系统的数据格式是json格式。

4、同时传入了nickname和marqueeName，在观看日志中，marqueeName将作为用户昵称的统计字段。

### 错误提示

1、user not found：请求自定义验证接口错误，或者接口返回的格式不对。

2、invalid sign：签名错误，sign的生成规则是secretkey+userid+secretkey+ts进行MD5加密。

3、sign expired：签名过期，每一个sign只能使用一次，使用后需重新生成。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://polyv.gitbook.io/document/docs/live/api/web/watch-condition/external_authorization_2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
