Java SDK 提供了 RegisterApp.register 方法,基于 OAuth 2.0 Device Authorization Grant(RFC 8628)协议实现一键创建应用。

调用该方法会返回一个验证链接,用户在飞书或 Lark 中打开该链接(或扫码)完成授权后,即可自动注册应用并获取凭据(App ID 和 App Secret),无需手动前往开发者后台创建。

查看源码:oapi-sdk-java

前提条件

  • 安装 oapi-sdk,且 SDK 版本为 2.6.1 及以上。

Maven:

  <dependency>
      <groupId>com.larksuite.oapi</groupId>
      <artifactId>oapi-sdk</artifactId>
      <version>2.6.1</version>
  </dependency>

快速开始示例

import com.lark.oapi.scene.registration.AppPreset;
import com.lark.oapi.scene.registration.RegisterApp;
import com.lark.oapi.scene.registration.RegisterAppException;
import com.lark.oapi.scene.registration.RegisterAppOptions;
import com.lark.oapi.scene.registration.RegisterAppResult;

public class Sample {

public static void main(String[] args) {
        try {
            RegisterAppResult result = RegisterApp.register(
                    RegisterAppOptions.newBuilder()
                            .source("test")
                            .appPreset(AppPreset.newBuilder()
                                    .avatars(
                                            "https://s1-imfile.feishucdn.com/static-resource/v1/v3_00cj_d6bebede-c56b-40a2-b767-8e9da07f3b3g",
                                            "https://s1-imfile.feishucdn.com/static-resource/v1/v2_bc5d2075-fcbd-41f8-bfe3-5a5ecbf0f7dg"
                                    )
                                    .name("{user}的应用")
                                    .desc("由业务平台自动生成")
                                    .build())
                            .onQRCode(info -> {
                                System.out.println("Please scan the QR code:");
                                System.out.println(info.getUrl());
                                System.out.println(String.format("Link expires in %s seconds", info.getExpireIn()));
                            })
                            .onStatusChange(info -> System.out.println(
                                    "status: " + info.getStatus()
                                            + (info.getInterval() > 0
                                            ? String.format(" (Interval: %s seconds)", info.getInterval())
                                            : "")
                            ))
                            .build()
            );

System.out.println("App ID: " + result.getClientId());
            System.out.println("App Secret: " + result.getClientSecret());
            System.out.println("User Info: " + result.getUserInfo());
        } catch (RegisterAppException e) {
            System.err.println("fail: " + e.getCode() + " " + e.getDescription());
        }
    }
}

真实可运行示例:

自定义权限/事件/回调与更新已有应用

创建应用时,可以通过 addons 在平台基础模板上增量申请权限、事件订阅和回调。这些配置会预填到用户打开验证链接后的确认页中,用户确认后生效:

import com.lark.oapi.scene.registration.AppAddons;

// 创建:增量申请权限/事件/回调,且只允许创建新应用。
RegisterApp.register(RegisterAppOptions.newBuilder()
        .addons(AppAddons.newBuilder()
                .tenantScopes("im:message:send_as_bot")
                .userScopes("calendar:calendar:read")
                .tenantEvents("im.message.receive_v1")
                .callbacks("card.action.trigger")
                .build())
        .createOnly(true)
        .onQRCode(info -> System.out.println(info.getUrl()))
        .build());

// 更新:传入已有应用的 appId,让用户确认增量配置变更。
RegisterApp.register(RegisterAppOptions.newBuilder()
        .appId("cli_xxx")
        .addons(AppAddons.newBuilder()
                .tenantScopes("drive:drive.metadata:readonly")
                .build())
        .onQRCode(info -> System.out.println(info.getUrl()))
        .build());

// 最小底座:preset(false) 把底座切换为最小基础模板,确认页只展示 addons 中显式声明的配置。
// 此时增量项可以为空。
RegisterApp.register(RegisterAppOptions.newBuilder()
        .addons(AppAddons.newBuilder()
                .preset(false)
                .tenantScopes("im:message:send_as_bot")
                .build())
        .onQRCode(info -> System.out.println(info.getUrl()))
        .build());
  • addons 仅支持增量叠加,不能删减基础模板里的权限。
  • addons.preset 控制底座模板:缺省或 true 保留平台默认模板(含默认权限/事件/回调);false 切换为最小基础模板(仅机器人能力,无业务权限),最终配置完全由 addons 声明。preset(false) 时增量项可以全部为空。
  • 仅支持 5 类公开配置:应用/用户身份权限、应用/用户身份事件、回调。事件请求 URL、security.*、加密 key 等敏感配置不能通过 addons 传入,需要使用更新应用开发配置 OpenAPI。
  • SDK 校验数据形状和非空值,不校验权限点/事件/回调名称是否存在于平台目录。

RegisterAppOptions 参数

参数 描述 类型 必须 默认
source 来源标识,拼入二维码 URL 的 source 参数,格式为 java-sdk/{source} String 否 -
domain 自定义飞书认证基地址 String 否 https://accounts.feishu.cn
larkDomain 自定义 Lark 认证基地址,检测到 Lark 租户时自动切换 String 否 https://accounts.larksuite.com
appPreset 预设应用信息,用于初始化应用创建页。所有字段都是可选的,用户扫码后仍可在页面手动修改。SDK 会自动对原始值做 URL Encode。 AppPreset 否 -
appPreset.avatar 应用头像候选 URL,支持 1-6 个;传多个时默认选中第一个。图片格式、GIF 截帧、裁切和展示由页面/服务端处理。 String / String[] / List<String> 否 -
appPreset.name 应用名称,支持 {user} 占位符,由应用创建页替换为扫码用户名称。 String 否 -
appPreset.desc 应用描述,支持 {user} 占位符。 String 否 -
addons 增量权限/事件/回调配置,预填到确认页。 AppAddons 否 -
addons.preset 底座模板开关。缺省或 true 保留平台默认模板;false 切换为最小基础模板,确认页只展示 addons 中显式声明的配置。 boolean 否 平台默认模板
addons.scopes.tenant 应用身份权限列表,例如 im:message:send_as_bot。 String[] / List<String> 否 -
addons.scopes.user 用户身份权限列表,例如 calendar:calendar:read。 String[] / List<String> 否 -
addons.events.items.tenant 应用身份事件列表,例如 im.message.receive_v1。 String[] / List<String> 否 -
addons.events.items.user 用户身份事件列表,例如 calendar.calendar.event.changed_v4。 String[] / List<String> 否 -
addons.callbacks.items 回调列表,例如 card.action.trigger。 String[] / List<String> 否 -
createOnly 为 true 时落地页仅允许创建新应用,并隐藏选择已有应用入口。 boolean 否 false
appId 已有应用的 App ID(cli_ 前缀)。传入后流程会让用户确认 addons 带来的配置 diff。createOnly 为 true 时页面会忽略该参数。 String 否 -
onQRCode 验证链接就绪时的回调,参数为 QRCodeInfo,包含 url 和 expireIn Consumer<QRCodeInfo> 是 -
onStatusChange 轮询状态变化时的回调,参数为 StatusChangeInfo Consumer<StatusChangeInfo> 否 -

返回值

字段 类型 描述
clientId String App ID
clientSecret String App Secret
userInfo UserInfo 扫码用户信息
userInfo.openId String 用户的 open_id
userInfo.tenantBrand String "feishu" 或 "lark"

状态回调

状态 描述
polling 仍在等待用户授权
slow_down 服务端要求降低轮询频率,interval 为新的轮询间隔(秒)
domain_switched SDK 检测到 Lark 租户并切换到 larkDomain 继续轮询

错误处理

RegisterAppException 包含 code 和 description 字段:

code 描述
access_denied 用户拒绝了授权
expired_token 二维码过期或轮询超时
abort 通过线程中断取消了注册流程
invalid_response 服务端返回了不符合预期的响应