Skip to content

SAAS 接入

无界云剪提供了接入第三方系统用户体系的功能,实现了与第三方系统用户与登录状态的互通。

一、接入原理

  • 云剪平台用户相关接口采用jwt token方式做用户身份认证
  • 用户映射:将第三方的用户在云剪平台中映射生成一个对应用户
  • 用户token:基于映射到云剪平台中的用户生成token,并通过url参数跳转进云剪系统(云剪平台会自动解析处理token)
  • 登录状态同步:
    • 第三方系统提供额外的接口用于云剪平台查询用户登录状态。
    • 云剪平台中每次调用用户相关接口时,除了校验自己平台内token状态,还会额外调用第三方提供的登录状态查询接口,只有都通过才算是已登录状态。

二、接入流程

1、获取 appid 和 secret

在无界云剪平台中管理后台-应用管理,创建应用,获取应用的appidsecret,具体获取方法请联系平台客服微信:mantousee

2、获取 token

调用平台接口获取token,其中appidsecret由平台提供。

注意:

  • 每个userId会在云剪项目中生成一个对应的用户(已存在则更新)
  • 云剪项目中用户的信息会在每次调用/api/v1/third/login接口时更新。
  • 云剪项目中的token有效期以及用户登录状态可能和第三方系统不一致,如需保持一致,第三方应该提供额外接口用于查询用户登录状态。

参考接口文档: https://docs.apipost.net/docs/detail/40f0d78218e0000?target_id=1fb5aee

参考代码

nodejs

js
  const crypto = require('crypto');
  const axios = require('axios');

  /**
   * md5加密
   * @param {string} str 
   * @returns 
   */
  function md5(str) {
    return crypto.createHash("md5").update(str).digest("hex");
  }

  /**
   * 获取token
   * @returns {user_id:”871668385574199296“, token:"Bearer eyJh***", expires_at:1758270091116}
   */
  async function getToken() {
    const timestamp = Date.now();//毫秒时间戳
    const appid = "";//【平台获取】应用appid
    const secret = "";//【平台获取】应用密钥
    const url = `https://video.h5ds.com/api/v1/third/login`;//接口地址
    const sign = md5(timestamp + secret);//签名
    
    //参数
    const data = {
      userId: "user_01", //【必选】业务方用户的唯一标识,建议使用用户ID
      nick_name: "wtone", //【可选】昵称
      mobile: "15888888889", //【可选】手机号码
      email: "71999911@qq.com", //【可选】电子邮箱,比如 example@qq.com
      avatar: "", //【可选】头像,应该是完整可访问的地址
      gender: 0, //【可选】性别  0-女  1-男  2-其他
    };
    //headers
    const headers = {
      appid,//应用appid
      sign,//【重要】签名算法
      timestamp,//毫秒时间戳
    };
    const response = await axios.post(url, data, { headers });
    //接口返回{code:0,message:"success",data:{}},code非0表示错误
    if (!response.data || response.data.code !== 0) {
      throw new Error(response.data.message);
    }

    //返回格式{user_id, token, expires_at}
    return response.data.data;
  }

  (async () => {
    const { user_id, token, expires_at } = await getToken();
    console.log("user_id:", user_id, "\n  token:", token, "\nexpires_at:", expires_at);
  })();

java

java
import okhttp3.*;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Token生成工具类
 */
public class SAAS {
    private static final String APP_ID = ""; // 【平台获取】应用appid
    private static final String SECRET = ""; // 【平台获取】应用密钥
    private static final String API_URL = "https://video.h5ds.com/api/v1/third/login"; // API接口地址

    /**
     * 获取Token
     * @return Token字符串
     * @throws Exception 请求异常或API返回错误
     */
    public static String getToken() throws Exception {
        // 生成时间戳
        long timestamp = System.currentTimeMillis();
        // 生成签名(MD5(时间戳+密钥))
        String sign = md5(timestamp + SECRET);

        // 初始化HTTP客户端
        OkHttpClient client = new OkHttpClient();

        // 构造请求体JSON
        String json = "{\"userId\":\"user_01\",\"nick_name\":\"wtone\"," +
                "\"mobile\":\"15888888889\",\"email\":\"71999911@qq.com\"," +
                "\"avatar\":\"\",\"gender\":0}";

        // 创建请求体
        RequestBody body = RequestBody.create(json, MediaType.parse("application/json"));

        // 构造请求头
        Request request = new Request.Builder()
                .url(API_URL) // 设置API地址
                .post(body) // 设置为POST请求
                .addHeader("appid", APP_ID) // 添加appid头
                .addHeader("sign", sign) // 添加签名头
                .addHeader("timestamp", String.valueOf(timestamp)) // 添加时间戳头
                .addHeader("Content-Type", "application/json") // 设置内容类型
                .build();

        // 执行请求并处理响应
        try (Response response = client.newCall(request).execute()) {
            // 检查响应状态
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code " + response);
            }

            // 获取响应数据
            String responseData = response.body().string();
            // TODO: 根据实际API响应结构解析token
            return responseData;
        }
    }

    /**
     * MD5加密方法
     * @param input 输入字符串
     * @return MD5哈希值
     * @throws NoSuchAlgorithmException
     */
    private static String md5(String input) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update(input.getBytes());
        byte[] digest = md.digest();
        StringBuilder sb = new StringBuilder();
        for (byte b : digest) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

    // 测试主方法
    public static void main(String[] args) {
        try {
            String token = getToken();
            System.out.println("Token: " + token);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

php

php
<?php
  /**
   * post请求
   */
  function post($url, $data, $headers)
  {
      $ch = curl_init($url);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
      curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(['Content-Type: application/json'], $headers));
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

      $response = curl_exec($ch);

      if (curl_errno($ch)) {
          throw new Exception('Request failed: ' . curl_error($ch));
      }

      $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);

      if ($httpCode != 200) {
          throw new Exception('API request failed with status: ' . $httpCode);
      }

      $responseData = json_decode($response, true);
      return $responseData;
  }
  /**
   * 获取token
   */
  function getToken()
  {
      $timestamp = round(microtime(true) * 1000); //毫秒时间戳
      $appid = "";//【平台获取】应用appid
      $secret = "";//【平台获取】应用密钥
      $url = 'https://video.h5ds.com/api/v1/third/login';//接口地址
      $sign = md5($timestamp . $secret);//签名

      //参数
      $data = [
          'userId' => 'user_01', //【必选】业务方用户的唯一标识,建议使用用户ID
          'nick_name' => 'wtone', //【可选】昵称
          'mobile' => '15888888889', //【可选】手机号码
          'email' => '71999911@qq.com', //【可选】电子邮箱,比如 example@qq.com
          'avatar' => '', //【可选】头像,应该是完整可访问的地址
          'gender' => 0 //【可选】性别  0-女  1-男  2-其他
      ];
      //headers
      $headers = [
          'appid: ' . $appid,   //应用appid
          'sign: ' . $sign,   //【重要】签名算法
          'timestamp: ' . $timestamp  //毫秒时间戳
      ];
      $responseData = post($url, $data, $headers);

      if (!$responseData || $responseData['code'] !== 0) {
          throw new Exception($responseData['message'] ?? 'Unknown error');
      }

      return $responseData['data'];
  }

  // 使用示例
  $tokenData = getToken();
  print_r($tokenData);

3、跳转到无界云剪项目

基于上面获取到的token,通过携带 query 参数跳转到无界云剪项目中,项目会自动解析query中的token参数并再后续的接口请求中使用该token。

jsx
<a href={`https://video.h5ds.com?token=${token}`} target="_blank" />

三、用户登录状态互通

由于第三方系统中用户登录状态和云剪项目中用户登录状态(token有效期)是完全隔离的两个信息,因此为了实现用户在第三方系统中退出登录、登录失效(过期),云剪系统用户token也自动失效退出,需要第三方系统提供额外接口用于查询用户登录状态。

1、第三方系统提供登录状态查询接口

接口地址

GET http://192.168.31.46:8080/api/v1/test/saas/userStatus?userID={userId}

参数

userID 第三方系统的用户ID(调用saas登录接口时传递的userId参数)

返回格式

json
{
  "status": 0, //用户登录状态 0-未登录 1-已登录
}

2、云剪项目配置

云剪项目需要配置登录状态查询接口,配置方式如下: server/.env

#【saas用户登录状态校验配置】
# host.saas=http://192.168.31.46:8080/api/v1/test/saas/userStatus

四、SAAS接口

saas级别的接口可以查看和管理整个应用范围内的用户和作品信息

接口参考文档:https://docs.apipost.net/docs/detail/40f0d78218e0000?target_id=1fb5aec

接口主要对Header中sign签名参数做身份权限校验

五、用户接口

实际上我们拿到 token 之后,可以请求项目中用户相关的全部 API 接口,比如获取项目列表,删除项目,更新项目等等。具体的 API 文档购买后会提供。

参考接口文档:https://docs.apipost.net/docs/detail/40f0d78218e0000?target_id=1fb58d4

接口主要对Header中的Authorization:{token}参数做身份权限校验。

接口样例

1、获取当前用户信息

ts
/**
 * 获取当前用户信息
 */
function getCurrentUserInfo(token) {
  return axios({
    method: "get",
    url: host + "/api/v1/user/info",
    headers: {
      Authorization: token,
    },
  }).then((res) => res.data.data);
}

// 获取当前用户信息
const userInfo = await getCurrentUserInfo(token);

2、 创建一个新的工程

获取到工程数据返回参数 res

ts
/**
 * 创建一个新的H5
 */
function createMovieData(token) {
  const ndata = {
    title: "未命名",
    createTime: +new Date(),
    updateTime: +new Date(),
    poster: "",
    width: 1920,
    height: 1080,
    background: {},
    transitions: [],
    cameras: [],
    captions: [],
    elements: [],
    resouces: [],
  };
  return axios({
    method: "post",
    url: host + "/api/v1/user/apps/create",
    headers: {
      Authorization: token,
    },
    data: {
      source_id: "", //来源Id
      category_id: 0, //分类Id
      name: ndata.title, //名称
      description: ndata.title, //描述
      duration: 0, //时长(毫秒)
      width: ndata.width, //宽度
      height: ndata.height, //高度
      thumb: "", //封面图url
      data: ndata,
    },
  }).then((res) => res.data.data);
}

// 创建视频
const res = await createMovieData(token);

Powered by 四川爱趣五科技有限公司.蜀ICP备18034069号.