最近有朋友问起我有没有做过抖音开放平台,让我有了些思考,其实之前做过的。虽然抖音APP很火,但是毕竟不像微信开放平台那样,已沉淀多年,基本上每个API只要肯用心查找,网上都有很多资料可以参考。而抖音开放平台则不然,刚面世不久,资料比较少。即使对于一个开发人员来说,接入第三方接口都大同小异,不会太难,但我还是想把这些记录下来,特别是遇到的坑,会列在下面,一起参考学习。限于水平有限,若有错误,不吝赐教哈。那么,我们就开始正文吧。

1、注册账号

抖音开放平台地址:
https://open.douyin.com/platform

无独有偶,和其他第三方平台一样,进入开放平台注册账号后登录,平台会审核提交的信息,审核过了再创建应用(如果审核不过,是不让你创建应用的)

2、创建应用

我们获取数据一般是用来做PC网站的,所以选择网站应用来创建,如实填写信息,等待审核。这里吐槽一下,和微信相比,抖音不管是小程序还是开放平台,审核的速度很慢,虽然在提交完信息后平台一般会提示三个工作日内审核,但是你可能还是要发邮件过去催。这里给大家看一下审核通过后的应用。


点击详情进入,看到如下内容,我们有了平台颁发的Client KeyClient Secret就可以开始撸代码了。

3、实现思路

也没什么特别的思路啦,就是引导用户扫描我们接入的二维码,用户在抖音APP端扫码确认或账号密码授权登录后,会重定向到我们的回调接口,并且附带授权临时票据(code),我们拿着code,以及ClientKey和ClientSecret等参数,通过API换取access_token,然后就可以通过access_token进行接口调用,获取用户基本信息及其他操作等。大致的流程就是这样子,接下来我们就来看一下实现的一些细节。

4、开发细节

4.1、选择资源中心 -> Open Api -> 账号授权及绑定 查看接口文档,

4.2、用户扫码授权,回调我们的接口,拿到code,再调用获取access_token的接口,也可以拿到用户对应的open_id,因为access_token是有时效性的,所以我们要做缓存,要在过期前先用refresh_token刷新延长access_token的有效期,又过期后只能让用户重新授权。

授权相关的service

private static final Logger logger = LoggerFactory.getLogger(OauthServiceImpl.class);private static final String OAUTH_STATE_SESSION_KEY = "OAUTH_STATE_SESSION_KEY";@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Value("${redis.key.douyinTokenKeyPrefix}")private String douyinTokenKeyPrefix;@Value("${redis.key.douyinRefreshTokenKeyPrefix}")private String douyinRefreshTokenKeyPrefix;@Value("${redis.key.douyinClientTokenKey}")private String douyinClientTokenKey;/*** 获取授权码(code)* @param clientKey* @param redirectUri* @param state* @return*/@Overridepublic String qrcodeAuth(String clientKey,String redirectUri,String state) {String requestUrl = Urls.BASE_URL+String.format(Urls.PERSON_CONNECT_URL,clientKey,redirectUri,state);    ShiroUtils.setSessionAttribute(OAUTH_STATE_SESSION_KEY,state);logger.info("qrConnect requestUrl=" + requestUrl);return requestUrl;}/*** 获取access_token* @param request* @param clientKey* @param clientSecret* @return*/@Overridepublic TokenResult accessToken(HttpServletRequest request,String clientKey,String clientSecret) {String code = request.getParameter("code");String state = request.getParameter("state");Object sessionState = SecurityUtils.getSubject().getSession().getAttribute(OAUTH_STATE_SESSION_KEY);TokenResult token = new TokenResult();//校验stateif (sessionState != null && state.equalsIgnoreCase(sessionState.toString())) {SecurityUtils.getSubject().getSession().removeAttribute(OAUTH_STATE_SESSION_KEY);String requestUrl = Urls.BASE_URL+String.format(Urls.ACCESS_TOKEN_URL,clientKey,clientSecret,code);JSONObject response = (CommonUtil.httpsRequestJson(requestUrl, "GET", null));JSONObject object = response.getJSONObject("data");logger.info("accessToken result=" + response);int errorCode = object.getInteger("error_code");String description = object.getString("description");if (errorCode == 0) {token.setErrorCode(0);token.setAccessToken(object.getString("access_token"));token.setExpiresIn(object.getInteger("expires_in"));token.setRefreshToken(object.getString("refresh_token"));token.setOpenId(object.getString("open_id"));token.setScope(object.getString("scope"));} else {token.setErrorCode(errorCode);token.setDescription(description);}} else {token.setErrorCode(500);token.setDescription("state校验失败");}return token;}/*** 刷新access_token* @param clientKey* @param refreshToken* @return*/@Overridepublic TokenResult refreshToken(String clientKey,String refreshToken) {String requestUrl = Urls.BASE_URL+String.format(Urls.REFRESH_TOKEN_URL,clientKey,refreshToken);JSONObject response = (CommonUtil.httpsRequestJson(requestUrl, "GET", null));JSONObject object = response.getJSONObject("data");logger.info("refreshToken result=" + response);int errorCode = object.getInteger("error_code");String description = object.getString("description");TokenResult token = new TokenResult();if (errorCode == 0) {token.setErrorCode(0);token.setAccessToken(object.getString("access_token"));token.setExpiresIn(object.getInteger("expires_in"));token.setRefreshToken(object.getString("refresh_token"));token.setOpenId(object.getString("open_id"));token.setScope(object.getString("scope"));} else {token.setErrorCode(errorCode);token.setDescription(description);}return token;}

授权相关的controller

 @Value("${redis.key.douyinTokenKeyPrefix}")private String douyinTokenKeyPrefix;@Value("${redis.key.douyinRefreshTokenKeyPrefix}")private String douyinRefreshTokenKeyPrefix;@Value("${open.douyin.clientKey}")private String clientKey;@Value("${open.douyin.clientSecret}")private String clientSecret;/*** 抖音授权登录* @param anchorUuid* @param response* @throws IOException*/@RequestMapping(value = "qrcodeAuth")public void qrcodeAuth(String anchorUuid, HttpServletResponse response) throws IOException {String redirectUrl = parameter.getSERVER_PATH() + "/mobile/douyin/authCallback";String state = UuidUtils.randomUUID() + "::" + anchorUuid;String requestUrl = oauthService.qrcodeAuth(clientKey, URLEncoder.encode(redirectUrl, "UTF-8"), state);response.sendRedirect(requestUrl);}/*** 抖音授权回调* @param request* @return*/@RequestMapping(value = "authCallback")public void authCallback(HttpServletRequest request) {String state = request.getParameter("state");String anchorUuid = state.split("::")[1];TokenResult result = oauthService.accessToken(request, clientKey, clientSecret);if (result.getErrorCode() == 0) {String openId = result.getOpenId();String accessToken = result.getAccessToken();//保存accessToken等信息到缓存stringRedisTemplate.opsForValue().set(douyinTokenKeyPrefix + anchorUuid,accessToken, 14, TimeUnit.DAYS);stringRedisTemplate.opsForValue().set(douyinRefreshTokenKeyPrefix + anchorUuid,result.getRefreshToken(), 29, TimeUnit.DAYS);logger.info("accessToken===" + accessToken);anchorService.saveDouyin(accessToken, anchorUuid, openId);}}

4.3、根据access_token和open_id就可以获取到该用户的基本信息和粉丝统计数据

/*** 获取用户信息* @param accessToken* @param openId* @return*/@Overridepublic JSONObject userInfo(String accessToken,String openId) {String requestUrl = Urls.BASE_URL+String.format(Urls.USERINFO_URL,accessToken,openId);JSONObject response = (CommonUtil.httpsRequestJson(requestUrl, "GET", null));JSONObject object = response.getJSONObject("data");logger.info("userInfo result=" + response);return object;}/*** 获取用户粉丝数据* @param accessToken* @param openId* @return*/@Overridepublic JSONObject fansData(String accessToken,String openId) {String requestUrl = Urls.BASE_URL+String.format(Urls.FANS_DATA_URL,accessToken,openId);JSONObject response = (CommonUtil.httpsRequestJson(requestUrl, "GET", null));JSONObject object = response.getJSONObject("data");logger.info("fansData result=" + response);return object;}

用户信息接口没有返回该用户的粉丝数,倒是在粉丝统计数据接口那边返回来粉丝数,可以在这边拿到粉丝数存到用户表,结合前端开发,把数据传给前端就可以显示出来了。这边叫了一个漂亮的小姐姐授权了,下面有短视频截图,你们就说好不好看吧。

用户基本信息

粉丝年龄分布、区域分布和性别分布

粉丝活跃分布

粉丝设备分布

粉丝兴趣分布

4.4、根据access_token和open_id就可以获取到该用户所有的抖音短视频数据

/*** 该接口用于分页获取用户所有视频的数据。返回的数据是实时的。* 列出已发布的视频* @param accessToken* @param openId* @param cursor* @param count* @return*/@Overridepublic JSONObject videoList(String accessToken,String openId,Long cursor,Integer count) {String requestUrl = Urls.BASE_URL+String.format(Urls.VIDEO_LIST_URL,accessToken,openId,cursor,count);JSONObject response = (CommonUtil.httpsRequestJson(requestUrl, "GET", null));logger.info("videoList result=" + response);return response;}

和粉丝数一样,开放平台没有提供接口直接获取用户的作品数、点赞数、总评论数、总分享数、平均点赞数、平均评论数、平均分享数,所以我们在获取到所有视频的时候要根据每条视频返回来的相应字段计算出这些数据再存到数据库,结合前端开发,把数据传给前端就可以显示出来了。

这里不得不吐槽一下,像粉丝数、作品数、点赞数、总评论数、总分享数等这些和用户相关的字段应该统计出来在用户信息那个接口就要返回来的,这样能给开发者省了很多时间,而且更符合常理,不知道抖音是怎么想的。

5、总结


5.1、看完这些代码之后,其实也不难,和对接其他第三方接口一样,只要照着文档写,总能调出结果来。现在看到的抖音开放平台文档是更新过的,看上去会比之前要好些,不管是版面、注意点还是参数注释都有改进,虽然还是没提供demo下载,但是增加了几种语言的接口调用样例虽然没有什么实际的作用,但是手心手背都是肉,还是知足些吧。

5.2、第一次对接新的第三方接口基本都有坑,大部分时候我们都寄希望于踩过坑的前人能够填好这些坑,给后来的人一些参考,少走弯路,节省时间,提高效率。起初抖音官方在飞书建了开放平台技术讨论群,可以在里面问问题,但是没多久上线了工单平台,要开发者有问题就提工单,就关掉了飞书群。有过对接第三方开发经验的应该都有感触,提交工单的途径来问问题的效率有多慢。下面就列出一些在开发过程中遇到的坑,小伙伴们感受一波。

1、问题:当时对接的时候,修改回调域名需要重新审核,不知道现在平台改过来没有。

解决:所以为了保险起见,还是一开始就填正确,这点微信开放平台修改回调域名不需要审核。

2、问题:在做OAuth 2.0授权时,scope传入多个,像这样scope=aweme.share,hotsearch,enterprise.data,user_info,fans.list,following.list,fans.data,video.create,video.delete,video.data,video.list,video.comment,总是报“权限非法”,我去掉一个就可以,我试了多次后猜想应该是scope太长了,最后一个权限被抖音截掉了(比如video.comment被截断变成了video.com,而video.com确实不是完整的权限,所以就报权限非法的错误)。

解决:然后向平台反应了,果然这是他们的一个bug,现在已经修复了。

3、问题:接口不稳定,有时候可以,有时候不可以。

解决:所有的接口路径,最后面都要加上“/”,比如/fans/data/,这个不知道为什么,是问了抖音工作人员给出的解决方案。

4、问题:调用授权二维码的时候,如果因为自身业务需要在用户扫码确认授权后回调我们的接口那边携带自己的参数,要注意,不能在回调接口的路径上拼接参数,因为回调那边获取不到,比如回调接口路径是/mobile/douyin/authCallback,不能这样携带参数/mobile/douyin/authCallback?userId=36781631,这应该也是被抖音限制了吧,但是做微信扫码授权就可以这样传参。

解决:扫码的时候为了安全需要传入一个随机数state,可以在state后面拼接我们的业务参数,然后在回调那边获取到state后截取。

抖音开放平台用户授权获取用户的粉丝统计和短视频数据相关推荐

  1. php实现抖音开放平台账号授权获取码code、获取access_token

    直接上代码 public function index(){$code = $_GET['code'];$dyClientKey = xxx;$dyClientSecret = xxx;if(empt ...

  2. 抖音开放平台网站应用:用户未绑定应用白名单,请授权trial.whitelist权限

    前言 升级后的抖音开放平台 2022-12-10日首稿 测试权限 开通测试权限需要做下面几件事情: 添加 user_info(用户权限栏下)权限 添加 trial.whitelist (特殊权限栏下) ...

  3. 抖音开放平台授权登录PHP,5分钟快速接入抖音开放平台,获取我的抖音粉丝列表,还有更多实用API...

    注:图片来自抖音开放平台 极速体验 即便你还没有抖音开放平台的账号,也可以极速体验一下抖音授权和接口调用的效果. 进入果创云开放平台-会员-抖音用户-抖音扫码授权. 链接:http://open.ye ...

  4. 抖音开放平台入门教程之获取抖音授权,根据授权换取token,根据token调用接口示例!

    一. 申请入驻抖音开放平台,按照项目需求申请个人账号和企业账号,企业账号权限相对来说高一点,企业号认证费用600元,具体可以打抖音官方电话400-992-2556根据提示选择对应平台进行咨询 二. 申 ...

  5. 抖音用户手机号API说明-------抖音开放平台

    在抖音开放平台里有这么一个接口是获取抖音用户的手机号的,在用户授权之后会得到一个由base64的加密的字符串,我们要想知道这个字符串代表的手机号是多少,就必须要进行解密. 一下是抖音开放平台给出的示例 ...

  6. Java实现抖音开放平台二维码授权功能

    一.在yml中添加抖音开放平台的账号基本信息 # 抖音开放平台配置信息 dy:clientKey: #抖音开放平台keyclientSecret: #抖音开放平台密钥scope: data.exter ...

  7. 抖音开放平台授权登录PHP,抖音向第三方平台开放“一键发布”功能 大疆、网易游戏等已接入...

    9月6日,据记者了解,抖音已经开放了第三方App的内容分享至抖音的功能,这意味着用户在第三方平台发布的内容可以直接同步至抖音. 据抖音方面介绍,分享功能在2019年3月开始上线,目前除各种视频工具类应 ...

  8. 抖音开放平台-视频切片-视频分片上传-不合法的参数ID-不合法的对象ID

    问题描述 1.最近遇到个问题,做业务需要管理几个抖音账号,用抖音开放平台做分片上传视频,多次返回不合法参数id,提交工单之后给的回复没有任何参考价值. 2.例如视频文件按15M进行切片,调用分片上传初 ...

  9. 抖音开放平台,究竟开放了什么?

    "抖音有 6 亿用户,我们希望连同更多的开发者在抖音里能够为用户交付更多.更优质的服务." --常坤 抖音开放平台负责人 作为日活超 6 亿的短视频平台,抖音已经渗透到我们生活的多 ...

最新文章

  1. IO之阻塞与非阻塞比较
  2. CSS 基础:文本和字体(4)思维导图
  3. 稀疏矩阵加法运算_1.2 震惊! 某大二本科生写的矩阵乘法吊打Mathematica-线性代数库BLAS-矩阵 (上)...
  4. 回顾 | 进击吧! Blazor !第三期 信息交互
  5. 将数据库日志添加到JUnit3
  6. html中如何计算图片的像素,html – 浏览器的1px计算问题(子像素问题)
  7. java.lang.IllegalStateException: ActionBarImpl can only be used with a compatible window decor layou
  8. ZK框架笔记3、窗体组件
  9. 在静态页面html中跳转传值
  10. 睡眠分期中的各种特征
  11. c语言贪吃蛇答辩项目,贪吃蛇项目V1答辩.PDF
  12. docker 安装wiki.js 和wekan
  13. bzoj1499(DP+单调队列)
  14. HTTP(9):新增功能协议
  15. Acer Linux改win7,宏基台式机win10如何改win7系统_宏基台式机预装win10怎么换win7
  16. 人类dna信息量_古人类DNA揭人类演化史 白肤碧眼1万年前才出现
  17. 动图之一个圆绕另一个圆转动
  18. 基于matlab的数字调制,基于MATLAB的多功能数字调制系统信号源仿真
  19. 实用的SEO必用的3款关键词查询工具
  20. 问题 L: 【分治】珍珠项链

热门文章

  1. 各种云服务器性能优秀强大,各家云服务器性能对比
  2. 打印机只能打印测试页
  3. 3.27 网易春招第一题--击杀怪物
  4. GB2312-80编码表
  5. android和ios比例,91分析Android与IOS游戏及软件下载比例数据
  6. seo网站要发外链找哪些平台?
  7. Shiro反序列化漏洞利用笔记
  8. 那些年我们电脑上的软件 都用过的绝对是大神
  9. 计算机等级和计算应用区别,全国计算机等级考试一级和二级的区别是什么?
  10. 校二级计算机考试内容,计算机二级考试科目及内容有哪些